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:
parent
a8b584a9d6
commit
c922dfb6fe
25 changed files with 201 additions and 174 deletions
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -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/
|
||||||
|
|
3
.github/workflows/deploy-master-staging.yaml
vendored
3
.github/workflows/deploy-master-staging.yaml
vendored
|
@ -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
|
||||||
|
@ -52,4 +53,4 @@ jobs:
|
||||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CLOUD_DEPLOYMENTS_WEBHOOK_URL }}
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CLOUD_DEPLOYMENTS_WEBHOOK_URL }}
|
||||||
JOB_TITLE: "Dashboard deployment to ${{ env.ENVIRONMENT }}"
|
JOB_TITLE: "Dashboard deployment to ${{ env.ENVIRONMENT }}"
|
||||||
run: |
|
run: |
|
||||||
python3 ./.github/workflows/notify/notify-slack.py
|
python3 ./.github/workflows/notify/notify-slack.py
|
||||||
|
|
11
.github/workflows/test-env-deploy.yml
vendored
11
.github/workflows/test-env-deploy.yml
vendored
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:-/}
|
||||||
|
|
|
@ -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']";
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 haven’t defined a gift card product!"
|
"string": "You haven’t 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"
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,18 +146,17 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
|
||||||
</Card>
|
</Card>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
|
|
||||||
<Card>
|
{(loading || data?.dataPrivacyUrl) && (
|
||||||
<CardTitle
|
<Card>
|
||||||
title={intl.formatMessage({
|
<CardTitle
|
||||||
id: "a55zOn",
|
title={intl.formatMessage({
|
||||||
defaultMessage: "Data privacy",
|
id: "a55zOn",
|
||||||
description: "section header"
|
defaultMessage: "Data privacy",
|
||||||
})}
|
description: "section header"
|
||||||
/>
|
})}
|
||||||
<CardContent>
|
/>
|
||||||
{!loading ? (
|
<CardContent>
|
||||||
<>
|
{!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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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 />
|
||||||
|
|
|
@ -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 = (
|
||||||
|
|
|
@ -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")}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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 && [
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
38
src/marketplace/index.tsx
Normal 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
15
src/marketplace/styles.ts
Normal 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
3
src/marketplace/urls.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const marketplaceSection = "/marketplace/";
|
||||||
|
|
||||||
|
export const marketplaceUrl = marketplaceSection;
|
|
@ -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
|
||||||
|
|
|
@ -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: "",
|
||||||
|
|
Loading…
Reference in a new issue