From 1fb44790588bb6c3b3489388c46d268d71b7920f Mon Sep 17 00:00:00 2001 From: Lukasz Ostrowski Date: Thu, 13 Oct 2022 17:13:08 +0200 Subject: [PATCH] Add quick app install from manifest (#2378) * Create a button that navigates to app install page * Add translations keys * Extract messages * Change type literal to enum * Update snapshots --- locale/defaultMessages.json | 13 +++ .../InstallWithManifestFormButton.tsx | 97 +++++++++++++++++++ .../InstallWithManifestFormButton/index.ts | 1 + .../InstalledApps/InstalledApps.tsx | 19 +++- src/apps/urls.ts | 3 +- .../__snapshots__/Stories.test.ts.snap | 48 +++++++++ 6 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 src/apps/components/InstallWithManifestFormButton/InstallWithManifestFormButton.tsx create mode 100644 src/apps/components/InstallWithManifestFormButton/index.ts diff --git a/locale/defaultMessages.json b/locale/defaultMessages.json index c2a3907b6..04d229ae4 100644 --- a/locale/defaultMessages.json +++ b/locale/defaultMessages.json @@ -3627,6 +3627,9 @@ "context": "product field", "string": "Charge Taxes" }, + "QVraQY": { + "string": "App manifest URL" + }, "QY7FSs": { "context": "button", "string": "create product type" @@ -6007,6 +6010,10 @@ "context": "set balance dialog subtitle", "string": "What would you like to set cards balance to. When you change the balance both values will be changed" }, + "kIXV5V": { + "context": "install with app manifest button", + "string": "Install with App Manifest" + }, "kIvvax": { "string": "Search Products..." }, @@ -6461,6 +6468,9 @@ "context": "dialog title", "string": "Delete Warehouse" }, + "o/q4fc": { + "string": "Usually ends with /api/manifest" + }, "o5KXAN": { "context": "delete webhook", "string": "Are you sure you want to delete {name}?" @@ -7247,6 +7257,9 @@ "context": "order history message", "string": "Order was confirmed" }, + "ubmFc8": { + "string": "Install" + }, "ud0w8h": { "context": "number of postal code ranges", "string": "{number} postal code ranges" diff --git a/src/apps/components/InstallWithManifestFormButton/InstallWithManifestFormButton.tsx b/src/apps/components/InstallWithManifestFormButton/InstallWithManifestFormButton.tsx new file mode 100644 index 000000000..988f50bfa --- /dev/null +++ b/src/apps/components/InstallWithManifestFormButton/InstallWithManifestFormButton.tsx @@ -0,0 +1,97 @@ +import { TextField } from "@material-ui/core"; +import { Button } from "@saleor/components/Button"; +import { makeStyles } from "@saleor/macaw-ui"; +import React, { useState } from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +enum AvailableStates { + Initial, + InputOpen, +} + +const useStyles = makeStyles( + theme => ({ + installButton: { + marginLeft: theme.spacing(2), + height: 52, + }, + }), + { + name: "InstallWithManifestFormButton", + }, +); + +interface Props { + onSubmitted(manifestUrl: string): void; +} + +export const InstallWithManifestFormButton = ({ onSubmitted }: Props) => { + const styles = useStyles(); + const intl = useIntl(); + + const [state, setState] = useState(AvailableStates.Initial); + + const handleFormSubmit: React.FormEventHandler = e => { + e.preventDefault(); + + const form = new FormData(e.currentTarget); + const inputValue = form.get("manifest-url") as string; + + try { + new URL(inputValue); + + onSubmitted(inputValue); + } catch (e) { + console.error("Invalid URL from input. Should be validated by browser"); + } + }; + + switch (state) { + case AvailableStates.Initial: { + return ( + + ); + } + case AvailableStates.InputOpen: { + return ( +
+ + + + ); + } + } +}; diff --git a/src/apps/components/InstallWithManifestFormButton/index.ts b/src/apps/components/InstallWithManifestFormButton/index.ts new file mode 100644 index 000000000..b17687c8f --- /dev/null +++ b/src/apps/components/InstallWithManifestFormButton/index.ts @@ -0,0 +1 @@ +export * from "./InstallWithManifestFormButton"; diff --git a/src/apps/components/InstalledApps/InstalledApps.tsx b/src/apps/components/InstalledApps/InstalledApps.tsx index 0c197ef6e..af066224c 100644 --- a/src/apps/components/InstalledApps/InstalledApps.tsx +++ b/src/apps/components/InstalledApps/InstalledApps.tsx @@ -6,18 +6,20 @@ import { TableRow, Typography, } from "@material-ui/core"; +import { InstallWithManifestFormButton } from "@saleor/apps/components/InstallWithManifestFormButton"; import { useAppListContext } from "@saleor/apps/context"; -import { appUrl } from "@saleor/apps/urls"; +import { appUrl, createAppInstallUrl } from "@saleor/apps/urls"; import CardTitle from "@saleor/components/CardTitle"; import { IconButton } from "@saleor/components/IconButton"; import { TableButtonWrapper } from "@saleor/components/TableButtonWrapper/TableButtonWrapper"; import TableRowLink from "@saleor/components/TableRowLink"; import { AppListItemFragment, AppsListQuery } from "@saleor/graphql"; +import useNavigator from "@saleor/hooks/useNavigator"; import { DeleteIcon, ResponsiveTable } from "@saleor/macaw-ui"; import { renderCollection } from "@saleor/misc"; import { ListProps } from "@saleor/types"; import clsx from "clsx"; -import React from "react"; +import React, { useCallback } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import { useStyles } from "../../styles"; @@ -37,6 +39,14 @@ const InstalledApps: React.FC = ({ const intl = useIntl(); const classes = useStyles(props); const { activateApp, deactivateApp } = useAppListContext(); + const navigate = useNavigator(); + + const navigateToAppInstallPage = useCallback( + (url: string) => { + navigate(createAppInstallUrl(url)); + }, + [navigate], + ); const getHandleToggle = (app: AppListItemFragment) => () => { if (app.isActive) { @@ -54,6 +64,11 @@ const InstalledApps: React.FC = ({ defaultMessage: "Third Party Apps", description: "section header", })} + toolbar={ + + } /> diff --git a/src/apps/urls.ts b/src/apps/urls.ts index cbc866387..316cc150a 100644 --- a/src/apps/urls.ts +++ b/src/apps/urls.ts @@ -54,7 +54,8 @@ export const appDeepPath = (id: string, subPath: string) => urlJoin(appPath(id), subPath); export const customAppPath = (id: string) => urlJoin(customAppListPath, id); export const appInstallPath = urlJoin(appsSection, "install"); -export const appInstallUrl = appInstallPath; +export const createAppInstallUrl = (manifestUrl: string) => + `${appInstallPath}?manifestUrl=${manifestUrl}`; export const appDetailsUrl = (id: string, params?: AppDetailsUrlQueryParams) => appDetailsPath(encodeURIComponent(id)) + "?" + stringifyQs(params); diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 3da1f24b4..0e18872b3 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -25459,6 +25459,22 @@ exports[`Storyshots Views / Apps / Apps list default 1`] = ` Third Party Apps +
+ +
+
+ +
+
+ +