Application Marketplace available in dashboard (#2054)

* Add marketplace

* Update messages

* Extract styles

* Update test env

* Update config and rename uri

* Update template

* Trigger CI

* Possible fix

* Update deploy yml

* Add marketplace to staging

* Fix responsiveness

* Trigger CI

* Fix navigation tests (#2081)

* fix navigate through shop

* wait for progress bar not to exist

* simplify code

* add wait

* Fix navigate through shop (#2076)

* fix navigate through shop

* wait for progress bar not to exist

* simplify code

* Refine App about section (#2056)

* Trigger CI

* Trigger CI

* Trigger CI

* Trigger CI

* Fix app activation/deactivation

* Hide configuration url and policy if theres none

* Remove about and support links

* Remove unused code

* Update stories and tests

* Update messages

* Fix privacy policy

* Fix activation and "open app"

* Update tests

Co-authored-by: Karolina Rakoczy <rakoczy.karolina@gmail.com>
This commit is contained in:
Wojciech Mista 2022-06-08 08:44:28 +02:00 committed by GitHub
parent a8b584a9d6
commit c922dfb6fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 201 additions and 174 deletions

View file

@ -25,3 +25,4 @@ greatly reduce the amount of work needed to review your work. -->
Modify API_URI if you want test instance to use custom backend. CYPRESS_API_URI is optional, use when necessary. --> Modify API_URI if you want test instance to use custom backend. CYPRESS_API_URI is optional, use when necessary. -->
API_URI=https://automation-dashboard.staging.saleor.cloud/graphql/ API_URI=https://automation-dashboard.staging.saleor.cloud/graphql/
MARKETPLACE_URL=https://marketplace-gray.vercel.app/

View file

@ -10,6 +10,7 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
env: env:
API_URI: /graphql/ API_URI: /graphql/
MARKETPLACE_URL: "https://marketplace-gray.vercel.app/"
APP_MOUNT_URI: /dashboard/ APP_MOUNT_URI: /dashboard/
STATIC_URL: /dashboard/static/ STATIC_URL: /dashboard/static/
SENTRY_ORG: saleor SENTRY_ORG: saleor

View file

@ -57,11 +57,21 @@ jobs:
pattern: (http|https)://[a-zA-Z0-9.-]+/graphql/? pattern: (http|https)://[a-zA-Z0-9.-]+/graphql/?
run: | run: |
echo "::set-output name=custom_api_uri::$(echo $pull_request_body | grep -Eo "$prefix$pattern" | sed s/$prefix// | head -n 1)" echo "::set-output name=custom_api_uri::$(echo $pull_request_body | grep -Eo "$prefix$pattern" | sed s/$prefix// | head -n 1)"
- name: Get MARKETPLACE_URL
id: marketplace_url
# Search for MARKETPLACE_URL in PR description
env:
pull_request_body: ${{ github.event.pull_request.body }}
prefix: MARKETPLACE_URL=
pattern: (http|https)://[a-zA-Z0-9.-]+/?
run: |
echo "::set-output name=custom_marketplace_url::$(echo $pull_request_body | grep -Eo "$prefix$pattern" | sed s/$prefix// | head -n 1)"
- name: Run build - name: Run build
env: env:
# Use custom API_URI or the default one # Use custom API_URI or the default one
API_URI: ${{ steps.api_uri.outputs.custom_api_uri || 'https://qa.staging.saleor.cloud/graphql/' }} API_URI: ${{ steps.api_uri.outputs.custom_api_uri || 'https://qa.staging.saleor.cloud/graphql/' }}
MARKETPLACE_URL: ${{ steps.marketplace_url.outputs.custom_marketplace_url }}
APP_MOUNT_URI: / APP_MOUNT_URI: /
STATIC_URL: / STATIC_URL: /
IS_CLOUD_INSTANCE: true IS_CLOUD_INSTANCE: true
@ -113,7 +123,6 @@ jobs:
env_url: https://${{ steps.set-domain.outputs.domain }}/storybook/index.html env_url: https://${{ steps.set-domain.outputs.domain }}/storybook/index.html
deployment_id: ${{ steps.storybook-deployment.outputs.deployment_id }} deployment_id: ${{ steps.storybook-deployment.outputs.deployment_id }}
cypress-run-critical: cypress-run-critical:
needs: deploy needs: deploy
runs-on: ubuntu-latest runs-on: ubuntu-latest

View file

@ -5,11 +5,12 @@ RUN npm install
COPY . . COPY . .
ARG APP_MOUNT_URI ARG APP_MOUNT_URI
ARG API_URI ARG API_URI
ARG MARKETPLACE_URL
ARG STATIC_URL ARG STATIC_URL
ENV API_URI ${API_URI:-http://localhost:8000/graphql/} ENV API_URI ${API_URI:-http://localhost:8000/graphql/}
ENV APP_MOUNT_URI ${APP_MOUNT_URI:-/dashboard/} ENV APP_MOUNT_URI ${APP_MOUNT_URI:-/dashboard/}
ENV STATIC_URL ${STATIC_URL:-/dashboard/} ENV STATIC_URL ${STATIC_URL:-/dashboard/}
RUN STATIC_URL=${STATIC_URL} API_URI=${API_URI} APP_MOUNT_URI=${APP_MOUNT_URI} npm run build RUN STATIC_URL=${STATIC_URL} API_URI=${API_URI} MARKETPLACE_URL=${MARKETPLACE_URL} APP_MOUNT_URI=${APP_MOUNT_URI} npm run build
FROM nginx:stable FROM nginx:stable
WORKDIR /app WORKDIR /app

View file

@ -5,6 +5,7 @@ RUN npm install
COPY . . COPY . .
ARG APP_MOUNT_URI ARG APP_MOUNT_URI
ARG API_URI ARG API_URI
ARG MARKETPLACE_URL
ARG STATIC_URL ARG STATIC_URL
ENV API_URI ${API_URI:-http://localhost:8000/graphql/} ENV API_URI ${API_URI:-http://localhost:8000/graphql/}
ENV APP_MOUNT_URI ${APP_MOUNT_URI:-/} ENV APP_MOUNT_URI ${APP_MOUNT_URI:-/}

View file

@ -5,6 +5,7 @@ export const LEFT_MENU_SELECTORS = {
home: "[data-test='menu-item-label'][data-test-id='home']", home: "[data-test='menu-item-label'][data-test-id='home']",
orders: "[data-test='menu-item-label'][data-test-id='orders']", orders: "[data-test='menu-item-label'][data-test-id='orders']",
discounts: "[data-test='menu-item-label'][data-test-id='discounts']", discounts: "[data-test='menu-item-label'][data-test-id='discounts']",
appSection: "[data-test='menu-item-label'][data-test-id='apps_section']",
app: "[data-test='menu-item-label'][data-test-id='apps']", app: "[data-test='menu-item-label'][data-test-id='apps']",
translations: "[data-test='menu-item-label'][data-test-id='translations']", translations: "[data-test='menu-item-label'][data-test-id='translations']",
customers: "[data-test='menu-item-label'][data-test-id='customers']" customers: "[data-test='menu-item-label'][data-test-id='customers']"
@ -22,3 +23,9 @@ export const CATALOG = {
categories: "[data-test='submenu-item-label'][data-test-id='categories']", categories: "[data-test='submenu-item-label'][data-test-id='categories']",
collections: "[data-test='submenu-item-label'][data-test-id='collections']" collections: "[data-test='submenu-item-label'][data-test-id='collections']"
}; };
export const APP_MENU_SELECTORS = {
app: "[data-test='submenu-item-label'][data-test-id='apps']"
};
export const appCommonSelector = "[data-test-id*='apps']";

View file

@ -7,9 +7,6 @@ const configurationAsParent = {
}; };
export const PERMISSIONS = { export const PERMISSIONS = {
app: {
permissionSelectors: [menuSelectors.LEFT_MENU_SELECTORS.app]
},
channel: { channel: {
parent: configurationAsParent, parent: configurationAsParent,
permissionSelectors: [CONFIGURATION_SELECTORS.channels] permissionSelectors: [CONFIGURATION_SELECTORS.channels]

View file

@ -9,7 +9,7 @@ export const PERMISSIONS_OPTIONS = {
}, },
app: { app: {
user: ONE_PERMISSION_USERS.app, user: ONE_PERMISSION_USERS.app,
permissions: [PERMISSIONS.app], permissions: [],
testCase: "TC: SALEOR_3402" testCase: "TC: SALEOR_3402"
}, },
channel: { channel: {

View file

@ -1,17 +1,55 @@
/// <reference types="cypress"/> /// <reference types="cypress"/>
/// <reference types="../support"/> /// <reference types="../support"/>
import {
APP_MENU_SELECTORS,
appCommonSelector,
LEFT_MENU_SELECTORS
} from "../elements/account/left-menu/left-menu-selectors";
import { PERMISSIONS_OPTIONS } from "../fixtures/permissionsUsers"; import { PERMISSIONS_OPTIONS } from "../fixtures/permissionsUsers";
import filterTests from "../support/filterTests"; import filterTests from "../support/filterTests";
import * as permissionsSteps from "../support/pages/permissionsPage"; import * as permissionsSteps from "../support/pages/permissionsPage";
describe("As a staff user I want to navigate through shop using different permissions", () => { describe("As a staff user I want to navigate through shop using different permissions", () => {
Object.keys(PERMISSIONS_OPTIONS).forEach(key => { const permissionsOptions = PERMISSIONS_OPTIONS;
before(() => {
cy.loginUserViaRequest()
.visit("/")
.get(appCommonSelector)
.should("be.visible")
.get("body")
.then($body => {
// This will be deleted when Marketplace is released
// Consider this solution as temporary
let appPermissions;
if ($body.find(LEFT_MENU_SELECTORS.appSection).length) {
appPermissions = {
parent: {
parentMenuSelector: LEFT_MENU_SELECTORS.appSection,
parentSelectors: [APP_MENU_SELECTORS]
},
permissionSelectors: [APP_MENU_SELECTORS.app]
};
} else {
appPermissions = {
permissionSelectors: [LEFT_MENU_SELECTORS.app]
};
}
permissionsOptions.all.permissions.push(appPermissions);
permissionsOptions.app.permissions = [appPermissions];
});
});
Object.keys(permissionsOptions).forEach(key => {
const tags = const tags =
key === "all" ? ["critical", "all", "refactored"] : ["all", "refactored"]; key === "all" ? ["critical", "all", "refactored"] : ["all", "refactored"];
filterTests({ definedTags: tags }, () => { filterTests({ definedTags: tags }, () => {
it(`should be able to navigate through shop as a staff member using ${key} permission. ${PERMISSIONS_OPTIONS[key].testCase}`, () => { it(`should be able to navigate through shop as a staff member using ${key} permission. ${permissionsOptions[key].testCase}`, () => {
const permissionOption = PERMISSIONS_OPTIONS[key]; const permissionOption = permissionsOptions[key];
const permissions = permissionOption.permissions; const permissions = permissionOption.permissions;
cy.clearSessionData(); cy.clearSessionData();
permissionsSteps.navigateToAllAvailablePageAndCheckIfDisplayed( permissionsSteps.navigateToAllAvailablePageAndCheckIfDisplayed(

View file

@ -1218,10 +1218,6 @@
"context": "WarehouseSettings public stock label", "context": "WarehouseSettings public stock label",
"string": "Public Stock" "string": "Public Stock"
}, },
"89PSdB": {
"context": "link",
"string": "Edit settings"
},
"8B8E+3": { "8B8E+3": {
"context": "navigator placeholder", "context": "navigator placeholder",
"string": "Order Number" "string": "Order Number"
@ -1929,6 +1925,10 @@
"context": "date time attribute type", "context": "date time attribute type",
"string": "Date Time" "string": "Date Time"
}, },
"E+M17x": {
"context": "marketplace section name",
"string": "Marketplace"
},
"E22x4H": { "E22x4H": {
"context": "no card defuned alert message", "context": "no card defuned alert message",
"string": "You havent defined a gift card product!" "string": "You havent defined a gift card product!"
@ -5700,10 +5700,6 @@
"hX5PAb": { "hX5PAb": {
"string": "No results found" "string": "No results found"
}, },
"hdcGSJ": {
"context": "button",
"string": "Support/FAQ"
},
"hkENym": { "hkENym": {
"string": "Customer" "string": "Customer"
}, },
@ -6204,10 +6200,6 @@
"llBnr+": { "llBnr+": {
"string": "Warehouse Name" "string": "Warehouse Name"
}, },
"llC1q8": {
"context": "button",
"string": "App home page"
},
"lm9NSK": { "lm9NSK": {
"context": "password reset, button", "context": "password reset, button",
"string": "Send an email with reset link" "string": "Send an email with reset link"

View file

@ -9,7 +9,6 @@ const props: AppDetailsPageProps = {
data: appDetails, data: appDetails,
loading: false, loading: false,
navigateToApp: () => undefined, navigateToApp: () => undefined,
navigateToAppSettings: () => undefined,
onAppActivateOpen: () => undefined, onAppActivateOpen: () => undefined,
onAppDeactivateOpen: () => undefined onAppDeactivateOpen: () => undefined
}; };

View file

@ -16,7 +16,6 @@ import { FormattedMessage, useIntl } from "react-intl";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
import activateIcon from "../../../../assets/images/activate-icon.svg"; import activateIcon from "../../../../assets/images/activate-icon.svg";
import settingsIcon from "../../../../assets/images/settings-icon.svg";
import supportIcon from "../../../../assets/images/support-icon.svg"; import supportIcon from "../../../../assets/images/support-icon.svg";
import { useStyles } from "../../styles"; import { useStyles } from "../../styles";
import DeactivatedText from "../DeactivatedText"; import DeactivatedText from "../DeactivatedText";
@ -25,7 +24,6 @@ export interface AppDetailsPageProps {
loading: boolean; loading: boolean;
data: AppQuery["app"]; data: AppQuery["app"];
navigateToApp: () => void; navigateToApp: () => void;
navigateToAppSettings: () => void;
onAppActivateOpen: () => void; onAppActivateOpen: () => void;
onAppDeactivateOpen: () => void; onAppDeactivateOpen: () => void;
} }
@ -34,7 +32,6 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
data, data,
loading, loading,
navigateToApp, navigateToApp,
navigateToAppSettings,
onAppActivateOpen, onAppActivateOpen,
onAppDeactivateOpen onAppDeactivateOpen
}) => { }) => {
@ -76,20 +73,6 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
description="link" description="link"
/> />
</ExternalLink> </ExternalLink>
{data.configurationUrl && (
<ButtonBase
className={classes.headerLinkContainer}
disableRipple
onClick={navigateToAppSettings}
>
<SVG src={settingsIcon} />
<FormattedMessage
id="89PSdB"
defaultMessage="Edit settings"
description="link"
/>
</ButtonBase>
)}
<ButtonBase <ButtonBase
className={classes.headerLinkContainer} className={classes.headerLinkContainer}
disableRipple disableRipple
@ -163,6 +146,7 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
</Card> </Card>
<CardSpacer /> <CardSpacer />
{(loading || data?.dataPrivacyUrl) && (
<Card> <Card>
<CardTitle <CardTitle
title={intl.formatMessage({ title={intl.formatMessage({
@ -173,8 +157,6 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
/> />
<CardContent> <CardContent>
{!loading ? ( {!loading ? (
<>
<Typography>{data?.dataPrivacy}</Typography>
<ExternalLink <ExternalLink
className={classes.linkContainer} className={classes.linkContainer}
href={data?.dataPrivacyUrl} href={data?.dataPrivacyUrl}
@ -186,12 +168,12 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
description="app privacy policy link" description="app privacy policy link"
/> />
</ExternalLink> </ExternalLink>
</>
) : ( ) : (
<Skeleton /> <Skeleton />
)} )}
</CardContent> </CardContent>
</Card> </Card>
)}
<CardSpacer /> <CardSpacer />
</Container> </Container>
); );

View file

@ -1,5 +1,6 @@
import useShop from "@saleor/hooks/useShop"; import useShop from "@saleor/hooks/useShop";
import { useTheme } from "@saleor/macaw-ui"; import { useTheme } from "@saleor/macaw-ui";
import clsx from "clsx";
import React from "react"; import React from "react";
import urlJoin from "url-join"; import urlJoin from "url-join";
@ -10,6 +11,7 @@ interface Props {
src: string; src: string;
appToken: string; appToken: string;
appId: string; appId: string;
className?: string;
onLoad?(): void; onLoad?(): void;
onError?(): void; onError?(): void;
} }
@ -20,6 +22,7 @@ export const AppFrame: React.FC<Props> = ({
src, src,
appToken, appToken,
appId, appId,
className,
onLoad, onLoad,
onError onError
}) => { }) => {
@ -55,7 +58,7 @@ export const AppFrame: React.FC<Props> = ({
src={urlJoin(src, `?domain=${shop.domain.host}&id=${appId}`)} src={urlJoin(src, `?domain=${shop.domain.host}&id=${appId}`)}
onError={onError} onError={onError}
onLoad={handleLoad} onLoad={handleLoad}
className={classes.iframe} className={clsx(classes.iframe, className)}
sandbox="allow-same-origin allow-forms allow-scripts" sandbox="allow-same-origin allow-forms allow-scripts"
/> />
); );

View file

@ -70,32 +70,6 @@ export const AppPage: React.FC<AppPageProps> = ({
description="button" description="button"
/> />
</Button> </Button>
<Button
component="a"
href={data?.homepageUrl}
variant="primary"
data-tc="open-app"
target="_blank"
>
<FormattedMessage
id="llC1q8"
defaultMessage="App home page"
description="button"
/>
</Button>
<Button
component="a"
href={data?.supportUrl}
variant="primary"
data-tc="open-support"
target="_blank"
>
<FormattedMessage
id="hdcGSJ"
defaultMessage="Support/FAQ"
description="button"
/>
</Button>
</div> </div>
</Grid> </Grid>
<CardSpacer /> <CardSpacer />

View file

@ -55,9 +55,6 @@ export const appInstallUrl = appInstallPath;
export const appDetailsUrl = (id: string, params?: AppDetailsUrlQueryParams) => export const appDetailsUrl = (id: string, params?: AppDetailsUrlQueryParams) =>
appDetailsPath(encodeURIComponent(id)) + "?" + stringifyQs(params); appDetailsPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
export const appSettingsUrl = (id: string, params?: AppDetailsUrlQueryParams) =>
appSettingsPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
export const appUrl = (id: string, params?: AppDetailsUrlQueryParams) => export const appUrl = (id: string, params?: AppDetailsUrlQueryParams) =>
appPath(encodeURIComponent(id)) + "?" + stringifyQs(params); appPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
export const appDeepUrl = ( export const appDeepUrl = (

View file

@ -16,9 +16,9 @@ import AppActivateDialog from "../../components/AppActivateDialog";
import AppDeactivateDialog from "../../components/AppDeactivateDialog"; import AppDeactivateDialog from "../../components/AppDeactivateDialog";
import AppDetailsPage from "../../components/AppDetailsPage"; import AppDetailsPage from "../../components/AppDetailsPage";
import { import {
appDetailsUrl,
AppDetailsUrlDialog, AppDetailsUrlDialog,
AppDetailsUrlQueryParams, AppDetailsUrlQueryParams,
appSettingsUrl,
appsListPath, appsListPath,
appUrl appUrl
} from "../../urls"; } from "../../urls";
@ -88,7 +88,7 @@ export const AppDetails: React.FC<AppDetailsProps> = ({ id, params }) => {
const [openModal, closeModal] = createDialogActionHandlers< const [openModal, closeModal] = createDialogActionHandlers<
AppDetailsUrlDialog, AppDetailsUrlDialog,
AppDetailsUrlQueryParams AppDetailsUrlQueryParams
>(navigate, params => appUrl(id, params), params); >(navigate, params => appDetailsUrl(id, params), params);
const handleActivateConfirm = () => { const handleActivateConfirm = () => {
activateApp(mutationOpts); activateApp(mutationOpts);
@ -121,7 +121,6 @@ export const AppDetails: React.FC<AppDetailsProps> = ({ id, params }) => {
data={data?.app} data={data?.app}
loading={loading} loading={loading}
navigateToApp={() => navigate(appUrl(id))} navigateToApp={() => navigate(appUrl(id))}
navigateToAppSettings={() => navigate(appSettingsUrl(id))}
onAppActivateOpen={() => openModal("app-activate")} onAppActivateOpen={() => openModal("app-activate")}
onAppDeactivateOpen={() => openModal("app-deactivate")} onAppDeactivateOpen={() => openModal("app-deactivate")}
/> />

View file

@ -11,12 +11,14 @@ import {
extensionMountPoints, extensionMountPoints,
useExtensions useExtensions
} from "@saleor/apps/useExtensions"; } from "@saleor/apps/useExtensions";
import { MARKETPLACE_URL } from "@saleor/config";
import { configurationMenuUrl } from "@saleor/configuration"; import { configurationMenuUrl } from "@saleor/configuration";
import { getConfigMenuItemsPermissions } from "@saleor/configuration/utils"; import { getConfigMenuItemsPermissions } from "@saleor/configuration/utils";
import { giftCardListUrl } from "@saleor/giftCards/urls"; import { giftCardListUrl } from "@saleor/giftCards/urls";
import { PermissionEnum, UserFragment } from "@saleor/graphql"; import { PermissionEnum, UserFragment } from "@saleor/graphql";
import { commonMessages, sectionNames } from "@saleor/intl"; import { commonMessages, sectionNames } from "@saleor/intl";
import { SidebarMenuItem } from "@saleor/macaw-ui"; import { SidebarMenuItem } from "@saleor/macaw-ui";
import { marketplaceUrl } from "@saleor/marketplace/urls";
import { pageListPath } from "@saleor/pages/urls"; import { pageListPath } from "@saleor/pages/urls";
import { IntlShape } from "react-intl"; import { IntlShape } from "react-intl";
@ -55,6 +57,42 @@ function useMenuStructure(
label: intl.formatMessage(sectionNames.appExtensions) label: intl.formatMessage(sectionNames.appExtensions)
}; };
// This will be deleted when Marketplace is released
// Consider this solution as temporary
const getAppSection = () => {
if (MARKETPLACE_URL) {
return {
ariaLabel: "apps_section",
iconSrc: appsIcon,
label: intl.formatMessage(sectionNames.apps),
permissions: [PermissionEnum.MANAGE_APPS],
id: "apps_section",
children: [
{
label: intl.formatMessage(sectionNames.apps),
id: "apps",
url: appsListPath
},
{
ariaLabel: "marketplace",
label: intl.formatMessage(sectionNames.marketplace),
id: "marketplace",
url: marketplaceUrl
}
]
};
}
return {
ariaLabel: "apps",
iconSrc: appsIcon,
label: intl.formatMessage(sectionNames.apps),
permissions: [PermissionEnum.MANAGE_APPS],
id: "apps",
url: appsListPath
};
};
const menuItems: FilterableMenuItem[] = [ const menuItems: FilterableMenuItem[] = [
{ {
ariaLabel: "home", ariaLabel: "home",
@ -202,14 +240,7 @@ function useMenuStructure(
id: "pages", id: "pages",
url: pageListPath url: pageListPath
}, },
{ getAppSection(),
ariaLabel: "apps",
iconSrc: appsIcon,
label: intl.formatMessage(sectionNames.apps),
permissions: [PermissionEnum.MANAGE_APPS],
id: "apps",
url: appsListPath
},
{ {
ariaLabel: "translations", ariaLabel: "translations",
children: extensions.NAVIGATION_TRANSLATIONS.length > 0 && [ children: extensions.NAVIGATION_TRANSLATIONS.length > 0 && [

View file

@ -8,6 +8,7 @@ export const APP_DEFAULT_URI = "/";
export const API_URI = process.env.API_URI; export const API_URI = process.env.API_URI;
export const SW_INTERVAL = parseInt(process.env.SW_INTERVAL, 10); export const SW_INTERVAL = parseInt(process.env.SW_INTERVAL, 10);
export const IS_CLOUD_INSTANCE = process.env.IS_CLOUD_INSTANCE === "true"; export const IS_CLOUD_INSTANCE = process.env.IS_CLOUD_INSTANCE === "true";
export const MARKETPLACE_URL = process.env.MARKETPLACE_URL;
export const DEFAULT_INITIAL_SEARCH_DATA: SearchVariables = { export const DEFAULT_INITIAL_SEARCH_DATA: SearchVariables = {
after: null, after: null,

View file

@ -47,6 +47,8 @@ import { giftCardsSectionUrlName } from "./giftCards/urls";
import { apolloClient, saleorClient } from "./graphql/client"; import { apolloClient, saleorClient } from "./graphql/client";
import HomePage from "./home"; import HomePage from "./home";
import { commonMessages } from "./intl"; import { commonMessages } from "./intl";
import MarketplaceSection from "./marketplace";
import { marketplaceUrl } from "./marketplace/urls";
import NavigationSection from "./navigation"; import NavigationSection from "./navigation";
import { navigationSection } from "./navigation/urls"; import { navigationSection } from "./navigation/urls";
import { NotFound } from "./NotFound"; import { NotFound } from "./NotFound";
@ -250,6 +252,11 @@ const Routes: React.FC = () => {
path={appsSection} path={appsSection}
component={AppsSection} component={AppsSection}
/> />
<SectionRoute
permissions={[PermissionEnum.MANAGE_APPS]}
path={marketplaceUrl}
component={MarketplaceSection}
/>
<SectionRoute <SectionRoute
permissions={[PermissionEnum.MANAGE_PRODUCTS]} permissions={[PermissionEnum.MANAGE_PRODUCTS]}
path={warehouseSection} path={warehouseSection}

View file

@ -382,6 +382,11 @@ export const sectionNames = defineMessages({
defaultMessage: "Home", defaultMessage: "Home",
description: "home section name" description: "home section name"
}, },
marketplace: {
id: "E+M17x",
defaultMessage: "Marketplace",
description: "marketplace section name"
},
navigation: { navigation: {
id: "9C7PZE", id: "9C7PZE",
defaultMessage: "Navigation", defaultMessage: "Navigation",

38
src/marketplace/index.tsx Normal file
View file

@ -0,0 +1,38 @@
import { Container } from "@material-ui/core";
import { AppFrame } from "@saleor/apps/components/AppFrame";
import NotFoundPage from "@saleor/components/NotFoundPage";
import { WindowTitle } from "@saleor/components/WindowTitle";
import { MARKETPLACE_URL } from "@saleor/config";
import useNavigator from "@saleor/hooks/useNavigator";
import { sectionNames } from "@saleor/intl";
import React from "react";
import { useIntl } from "react-intl";
import { useStyles } from "./styles";
const Component = () => {
const classes = useStyles();
const intl = useIntl();
const navigate = useNavigator();
if (!MARKETPLACE_URL) {
return <NotFoundPage onBack={() => navigate("/")} />;
}
return (
<>
<WindowTitle title={intl.formatMessage(sectionNames.marketplace)} />
<Container>
<AppFrame
src={MARKETPLACE_URL}
// Marketplace doesn't require app token nor id
appToken=""
appId=""
className={classes.iframe}
/>
</Container>
</>
);
};
export default Component;

15
src/marketplace/styles.ts Normal file
View file

@ -0,0 +1,15 @@
import { makeStyles } from "@saleor/macaw-ui";
const useStyles = makeStyles(
() => ({
iframe: {
height: "100vh",
position: "sticky"
}
}),
{
name: "marketplaceStyles"
}
);
export { useStyles };

3
src/marketplace/urls.ts Normal file
View file

@ -0,0 +1,3 @@
const marketplaceSection = "/marketplace/";
export const marketplaceUrl = marketplaceSection;

View file

@ -24699,34 +24699,6 @@ exports[`Storyshots Views / Apps / App default 1`] = `
About About
</span> </span>
</button> </button>
<a
aria-disabled="false"
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id MuiButton-containedPrimary-id"
data-tc="open-app"
href="http://localhost:8888/homepage"
tabindex="0"
target="_blank"
>
<span
class="MuiButton-label-id"
>
App home page
</span>
</a>
<a
aria-disabled="false"
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id MuiButton-containedPrimary-id"
data-tc="open-support"
href="http://localhost:8888/support"
tabindex="0"
target="_blank"
>
<span
class="MuiButton-label-id"
>
Support/FAQ
</span>
</a>
</div> </div>
</div> </div>
<div <div
@ -24806,13 +24778,6 @@ exports[`Storyshots Views / Apps / App details default 1`] = `
Get Support Get Support
</div> </div>
</a> </a>
<button
class="MuiButtonBase-root-id AppList-headerLinkContainer-id"
tabindex="0"
type="button"
>
Edit settings
</button>
<button <button
class="MuiButtonBase-root-id AppList-headerLinkContainer-id" class="MuiButtonBase-root-id AppList-headerLinkContainer-id"
tabindex="0" tabindex="0"
@ -24910,11 +24875,6 @@ exports[`Storyshots Views / Apps / App details default 1`] = `
<div <div
class="MuiCardContent-root-id" class="MuiCardContent-root-id"
> >
<div
class="MuiTypography-root-id MuiTypography-body1-id"
>
Lorem ipsum
</div>
<a <a
class="ExternalLink-link-id" class="ExternalLink-link-id"
href="http://localhost:8888/app-data-privacy" href="http://localhost:8888/app-data-privacy"
@ -24993,13 +24953,6 @@ exports[`Storyshots Views / Apps / App details loading 1`] = `
Get Support Get Support
</div> </div>
</a> </a>
<button
class="MuiButtonBase-root-id AppList-headerLinkContainer-id"
tabindex="0"
type="button"
>
Edit settings
</button>
<button <button
class="MuiButtonBase-root-id AppList-headerLinkContainer-id" class="MuiButtonBase-root-id AppList-headerLinkContainer-id"
tabindex="0" tabindex="0"
@ -25144,34 +25097,6 @@ exports[`Storyshots Views / Apps / App settings 1`] = `
About About
</span> </span>
</button> </button>
<a
aria-disabled="false"
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id MuiButton-containedPrimary-id"
data-tc="open-app"
href="http://localhost:8888/homepage"
tabindex="0"
target="_blank"
>
<span
class="MuiButton-label-id"
>
App home page
</span>
</a>
<a
aria-disabled="false"
class="MuiButtonBase-root-id MuiButton-root-id MuiButton-contained-id MuiButton-containedPrimary-id"
data-tc="open-support"
href="http://localhost:8888/support"
tabindex="0"
target="_blank"
>
<span
class="MuiButton-label-id"
>
Support/FAQ
</span>
</a>
</div> </div>
</div> </div>
<div <div

View file

@ -38,6 +38,7 @@ const htmlWebpackPlugin = new HtmlWebpackPlugin({
}); });
const environmentPlugin = new webpack.EnvironmentPlugin({ const environmentPlugin = new webpack.EnvironmentPlugin({
API_URI: "", API_URI: "",
MARKETPLACE_URL: "",
APP_MOUNT_URI: "/", APP_MOUNT_URI: "/",
DEMO_MODE: false, DEMO_MODE: false,
ENVIRONMENT: "", ENVIRONMENT: "",