Remove app list view from old apps dir (#3241)
This commit is contained in:
parent
a7d37ecd46
commit
ffa44f8b85
89 changed files with 243 additions and 2122 deletions
|
@ -86,10 +86,6 @@
|
|||
"context": "button",
|
||||
"string": "Activate"
|
||||
},
|
||||
"+c/f61": {
|
||||
"context": "retry installation",
|
||||
"string": "Retry"
|
||||
},
|
||||
"+do3gl": {
|
||||
"context": "input helper text",
|
||||
"string": "This number defines quantity of items in checkout line that can be bought. You can override this setting per variant. Leaving this setting empty mean that there is no limits."
|
||||
|
@ -117,9 +113,6 @@
|
|||
"context": "dialog header",
|
||||
"string": "Change Password"
|
||||
},
|
||||
"+niGip": {
|
||||
"string": "Saleor Apps"
|
||||
},
|
||||
"+pLi+M": {
|
||||
"context": "issued by app label",
|
||||
"string": "Issued by app"
|
||||
|
@ -510,10 +503,6 @@
|
|||
"1n1tOR": {
|
||||
"string": "Group is out of your permission scope"
|
||||
},
|
||||
"1qRwgQ": {
|
||||
"context": "app installation",
|
||||
"string": "Installing app..."
|
||||
},
|
||||
"1rpzrM": {
|
||||
"context": "search placeholder",
|
||||
"string": "Search by country name"
|
||||
|
@ -1502,10 +1491,6 @@
|
|||
"context": "unapproved fulfillment, section header",
|
||||
"string": "Waiting for approval ({quantity})"
|
||||
},
|
||||
"9tgY4G": {
|
||||
"context": "apps content",
|
||||
"string": "You don’t have any installed apps in your dashboard"
|
||||
},
|
||||
"9uNz+T": {
|
||||
"context": "button",
|
||||
"string": "Cancel"
|
||||
|
@ -1769,10 +1754,6 @@
|
|||
"context": "alert message",
|
||||
"string": "There are no available shipping methods in this channel."
|
||||
},
|
||||
"BvmnJq": {
|
||||
"context": "section header",
|
||||
"string": "Third Party Apps"
|
||||
},
|
||||
"Bx367s": {
|
||||
"context": "product is hidden",
|
||||
"string": "Hidden"
|
||||
|
@ -2210,9 +2191,6 @@
|
|||
"context": "dialog content",
|
||||
"string": "Select method you want to use to change address"
|
||||
},
|
||||
"FLtdaw": {
|
||||
"string": "Saleor apps are hosted and maintained by Saleor Team. They are preinstalled for you and ready to use"
|
||||
},
|
||||
"FNAZoh": {
|
||||
"string": "Last login"
|
||||
},
|
||||
|
@ -2772,9 +2750,6 @@
|
|||
"context": "currency code select",
|
||||
"string": "{code} - {countries}"
|
||||
},
|
||||
"J8frvS": {
|
||||
"string": "3rd party apps"
|
||||
},
|
||||
"JDz5h8": {
|
||||
"context": "number of subcategories in category",
|
||||
"string": "Subcategories"
|
||||
|
@ -3195,9 +3170,6 @@
|
|||
"context": "remove country from shipping zone and save, button",
|
||||
"string": "Remove and save"
|
||||
},
|
||||
"MYA6EV": {
|
||||
"string": "Third party apps are installed with App Manifests. They contain UI accessible from dashboard and can extend it."
|
||||
},
|
||||
"MaTR88": {
|
||||
"context": "header",
|
||||
"string": "Integrations"
|
||||
|
@ -3590,10 +3562,6 @@
|
|||
"context": "window title",
|
||||
"string": "Create Product"
|
||||
},
|
||||
"PbQJY5": {
|
||||
"context": "section header",
|
||||
"string": "Saleor Apps"
|
||||
},
|
||||
"PbqNhi": {
|
||||
"context": "order status",
|
||||
"string": "Partially fulfilled"
|
||||
|
@ -3753,9 +3721,6 @@
|
|||
"context": "product field",
|
||||
"string": "Charge Taxes"
|
||||
},
|
||||
"QVraQY": {
|
||||
"string": "App manifest URL"
|
||||
},
|
||||
"QZVD+5": {
|
||||
"context": "button, unassign attribute from object",
|
||||
"string": "Unassign and save"
|
||||
|
@ -3776,9 +3741,6 @@
|
|||
"context": "number of collections",
|
||||
"string": "Collections ({quantity})"
|
||||
},
|
||||
"QdQ9z7": {
|
||||
"string": "(TUNNEL - DEVELOPMENT)"
|
||||
},
|
||||
"Qe4XHv": {
|
||||
"context": "years after label",
|
||||
"string": "years after issue"
|
||||
|
@ -4752,10 +4714,6 @@
|
|||
"context": "filters error messages value required",
|
||||
"string": "Choose a value"
|
||||
},
|
||||
"Xl0o2y": {
|
||||
"context": "app installation error",
|
||||
"string": "Problem occured during installation"
|
||||
},
|
||||
"XlPKAR": {
|
||||
"context": "WarehouseSettings private stock description",
|
||||
"string": "If enabled stock in this warehouse won't be shown"
|
||||
|
@ -6240,10 +6198,6 @@
|
|||
"kFkPWB": {
|
||||
"string": "Number"
|
||||
},
|
||||
"kIXV5V": {
|
||||
"context": "install with app manifest button",
|
||||
"string": "Install with App Manifest"
|
||||
},
|
||||
"kIvvax": {
|
||||
"string": "Search Products..."
|
||||
},
|
||||
|
@ -6726,9 +6680,6 @@
|
|||
"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}?"
|
||||
|
@ -7468,9 +7419,6 @@
|
|||
"u0V06N": {
|
||||
"string": "Max. Order Weight"
|
||||
},
|
||||
"u0VQMN": {
|
||||
"string": "Browse Marketplace"
|
||||
},
|
||||
"u24Ppd": {
|
||||
"string": "This attribute cannot be assigned to this product type"
|
||||
},
|
||||
|
@ -7541,9 +7489,6 @@
|
|||
"context": "order history message",
|
||||
"string": "Order was confirmed"
|
||||
},
|
||||
"ubmFc8": {
|
||||
"string": "Install"
|
||||
},
|
||||
"uccjUM": {
|
||||
"context": "Dry run objects",
|
||||
"string": "Objects"
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
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 +0,0 @@
|
|||
export * from "./AppDialog";
|
|
@ -1,19 +0,0 @@
|
|||
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,60 +0,0 @@
|
|||
import ActionDialog from "@dashboard/components/ActionDialog";
|
||||
import { getStringOrPlaceholder } from "@dashboard/misc";
|
||||
import { DialogContentText } from "@material-ui/core";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
export interface AppInProgressDeleteDialogProps {
|
||||
confirmButtonState: ConfirmButtonTransitionState;
|
||||
open: boolean;
|
||||
name: string;
|
||||
onClose: () => void;
|
||||
onConfirm: () => void;
|
||||
}
|
||||
|
||||
const AppInProgressDeleteDialog: React.FC<AppInProgressDeleteDialogProps> = ({
|
||||
confirmButtonState,
|
||||
open,
|
||||
name,
|
||||
onClose,
|
||||
onConfirm,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<ActionDialog
|
||||
confirmButtonState={confirmButtonState}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onConfirm={onConfirm}
|
||||
title={intl.formatMessage({
|
||||
id: "zQX6xO",
|
||||
defaultMessage: "Delete App",
|
||||
description: "dialog header",
|
||||
})}
|
||||
variant="delete"
|
||||
>
|
||||
<DialogContentText>
|
||||
{["", null].includes(name) ? (
|
||||
<FormattedMessage
|
||||
id="6hLZNA"
|
||||
defaultMessage="Are you sure you want to delete this app?"
|
||||
description="delete app"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="EWD/wU"
|
||||
defaultMessage="Deleting {name}, you will remove installation of the app. If you are paying for app subscription, remember to unsubscribe from the app in Saleor Marketplace. Are you sure you want to delete the app?"
|
||||
description="delete app"
|
||||
values={{
|
||||
name: <strong>{getStringOrPlaceholder(name)}</strong>,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</DialogContentText>
|
||||
</ActionDialog>
|
||||
);
|
||||
};
|
||||
AppInProgressDeleteDialog.displayName = "AppInProgressDeleteDialog";
|
||||
export default AppInProgressDeleteDialog;
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./AppInProgressDeleteDialog";
|
||||
export { default } from "./AppInProgressDeleteDialog";
|
|
@ -1,72 +0,0 @@
|
|||
import { Typography } from "@material-ui/core";
|
||||
import { CopyIcon, makeStyles, Tooltip } from "@saleor/macaw-ui";
|
||||
import clsx from "clsx";
|
||||
import React, { useState } from "react";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
"@keyframes pulse": {
|
||||
from: { transform: "scale(1)" },
|
||||
to: { transform: "scale(1.2)" },
|
||||
},
|
||||
manifestText: {
|
||||
color: theme.palette.text.secondary,
|
||||
"&:hover svg": {
|
||||
visibility: "visible",
|
||||
},
|
||||
},
|
||||
copyIcon: {
|
||||
marginRight: theme.spacing(1),
|
||||
visibility: "hidden",
|
||||
verticalAlign: "middle",
|
||||
transition: "0.2s",
|
||||
},
|
||||
copyIconColorful: {
|
||||
color: theme.palette.primary.main,
|
||||
animation: "$pulse 0.2s",
|
||||
},
|
||||
}),
|
||||
{ name: "AppManifestTableDisplay" },
|
||||
);
|
||||
|
||||
interface AppManifestTableDisplayProps {
|
||||
manifestUrl: string;
|
||||
}
|
||||
|
||||
const getAppDomainFromManifest = (manifest: string) => new URL(manifest).host;
|
||||
|
||||
export const AppManifestTableDisplay = ({
|
||||
manifestUrl,
|
||||
}: AppManifestTableDisplayProps) => {
|
||||
const styles = useStyles();
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
return (
|
||||
<Tooltip placement="top" title={manifestUrl} header="App Manifest URL">
|
||||
<Typography
|
||||
onMouseOut={() => setCopied(false)}
|
||||
className={styles.manifestText}
|
||||
onClick={e => {
|
||||
try {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
navigator.clipboard.writeText(manifestUrl);
|
||||
setCopied(true);
|
||||
} catch (e) {
|
||||
// Copy not supported, ignore
|
||||
}
|
||||
}}
|
||||
>
|
||||
{!!navigator.clipboard && (
|
||||
<CopyIcon
|
||||
className={clsx(styles.copyIcon, {
|
||||
[styles.copyIconColorful]: copied,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{getAppDomainFromManifest(manifestUrl)}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
|
@ -1,43 +0,0 @@
|
|||
import { AppPagePathSegment } from "@dashboard/apps/hooks/useAppsPageNavigation";
|
||||
import { PageTab, PageTabs } from "@saleor/macaw-ui";
|
||||
import React, { ComponentProps } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
/**
|
||||
* Bind tab value to path segment to avoid unnecessary mapping
|
||||
*/
|
||||
const TabValue: Record<string, AppPagePathSegment> = {
|
||||
SALEOR_APPS: "saleor-apps",
|
||||
THIRD_PARTY: "third-party",
|
||||
};
|
||||
|
||||
type AllProps = ComponentProps<typeof PageTabs>;
|
||||
type AvailableProps = Omit<AllProps, "children" | "onChange" | "value"> & {
|
||||
value: AppPagePathSegment;
|
||||
showSaleorApps: boolean;
|
||||
onChange(newValue: AppPagePathSegment): void;
|
||||
};
|
||||
|
||||
export const AppPageTabs = ({ showSaleorApps, ...props }: AvailableProps) => {
|
||||
const intl = useIntl();
|
||||
return (
|
||||
<PageTabs {...props}>
|
||||
<PageTab
|
||||
value={TabValue.THIRD_PARTY}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "3rd party apps",
|
||||
id: "J8frvS",
|
||||
})}
|
||||
/>
|
||||
{showSaleorApps && (
|
||||
<PageTab
|
||||
value={TabValue.SALEOR_APPS}
|
||||
label={intl.formatMessage({
|
||||
defaultMessage: "Saleor Apps",
|
||||
id: "+niGip",
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</PageTabs>
|
||||
);
|
||||
};
|
|
@ -1,49 +0,0 @@
|
|||
import { AppPermissionFragment } from "@dashboard/graphql";
|
||||
import {
|
||||
IconButton,
|
||||
makeStyles,
|
||||
PermissionsIcon,
|
||||
Tooltip,
|
||||
} from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
() => ({
|
||||
list: {
|
||||
margin: 0,
|
||||
paddingLeft: "16px",
|
||||
},
|
||||
}),
|
||||
{ name: "AppPermissions" },
|
||||
);
|
||||
|
||||
interface AppPermissionsProps {
|
||||
permissions: AppPermissionFragment[];
|
||||
}
|
||||
|
||||
export const AppPermissions = ({ permissions }: AppPermissionsProps) => {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Tooltip
|
||||
header={
|
||||
<FormattedMessage
|
||||
defaultMessage="App permissions"
|
||||
id="xNfh4L"
|
||||
description="app permissions tooltip header"
|
||||
/>
|
||||
}
|
||||
title={
|
||||
<ul className={classes.list}>
|
||||
{permissions.map(permission => (
|
||||
<li key={permission.code}>{permission.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
}
|
||||
>
|
||||
<IconButton variant="secondary" color="primary">
|
||||
<PermissionsIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
|
@ -1,134 +0,0 @@
|
|||
import { Button } from "@dashboard/components/Button";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import { IconButton } from "@dashboard/components/IconButton";
|
||||
import { TableButtonWrapper } from "@dashboard/components/TableButtonWrapper/TableButtonWrapper";
|
||||
import TableRowLink from "@dashboard/components/TableRowLink";
|
||||
import { AppsInstallationsQuery, JobStatusEnum } from "@dashboard/graphql";
|
||||
import { renderCollection } from "@dashboard/misc";
|
||||
import {
|
||||
Card,
|
||||
CircularProgress as Progress,
|
||||
TableBody,
|
||||
TableCell,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import {
|
||||
DeleteIcon,
|
||||
Indicator,
|
||||
ResponsiveTable,
|
||||
Tooltip,
|
||||
TooltipMountWrapper,
|
||||
} from "@saleor/macaw-ui";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import { useStyles } from "../../styles";
|
||||
|
||||
export interface AppsInProgressProps {
|
||||
appsList: AppsInstallationsQuery["appsInstallations"];
|
||||
onAppInstallRetry: (id: string) => void;
|
||||
onRemove: (id: string) => void;
|
||||
}
|
||||
|
||||
const AppsInProgress: React.FC<AppsInProgressProps> = ({
|
||||
appsList,
|
||||
onAppInstallRetry,
|
||||
onRemove,
|
||||
...props
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles(props);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
id: "nIrjSR",
|
||||
defaultMessage: "Ongoing Installations",
|
||||
description: "section header",
|
||||
})}
|
||||
/>
|
||||
<ResponsiveTable>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
appsList,
|
||||
({
|
||||
status,
|
||||
appName,
|
||||
id,
|
||||
message,
|
||||
}: AppsInstallationsQuery["appsInstallations"][number]) => (
|
||||
<TableRowLink key={id} className={classes.tableRow}>
|
||||
<TableCell className={classes.colName}>
|
||||
<span data-tc="name">{appName}</span>
|
||||
</TableCell>
|
||||
{status === JobStatusEnum.PENDING && (
|
||||
<TableCell
|
||||
className={clsx(
|
||||
classes.colAction,
|
||||
classes.colInstallAction,
|
||||
)}
|
||||
>
|
||||
<Typography variant="body2" className={classes.text}>
|
||||
<FormattedMessage
|
||||
id="1qRwgQ"
|
||||
defaultMessage="Installing app..."
|
||||
description="app installation"
|
||||
/>
|
||||
</Typography>
|
||||
<div className={classes.colSpinner}>
|
||||
<Progress size={20} />
|
||||
</div>
|
||||
</TableCell>
|
||||
)}
|
||||
{status === JobStatusEnum.FAILED && (
|
||||
<TableCell
|
||||
className={clsx(
|
||||
classes.colAction,
|
||||
classes.colInstallAction,
|
||||
)}
|
||||
>
|
||||
<Typography variant="body2" className={classes.error}>
|
||||
<FormattedMessage
|
||||
id="Xl0o2y"
|
||||
defaultMessage="Problem occured during installation"
|
||||
description="app installation error"
|
||||
/>
|
||||
<Tooltip title={message} variant="error">
|
||||
<TooltipMountWrapper>
|
||||
<Indicator icon="error" />
|
||||
</TooltipMountWrapper>
|
||||
</Tooltip>
|
||||
</Typography>
|
||||
<TableButtonWrapper>
|
||||
<Button onClick={() => onAppInstallRetry(id)}>
|
||||
<FormattedMessage
|
||||
id="+c/f61"
|
||||
defaultMessage="Retry"
|
||||
description="retry installation"
|
||||
/>
|
||||
</Button>
|
||||
</TableButtonWrapper>
|
||||
<TableButtonWrapper>
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
color="primary"
|
||||
onClick={() => onRemove(id)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableButtonWrapper>
|
||||
</TableCell>
|
||||
)}
|
||||
</TableRowLink>
|
||||
),
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
AppsInProgress.displayName = "AppsInProgress";
|
||||
export default AppsInProgress;
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./AppsInProgress";
|
||||
export { default } from "./AppsInProgress";
|
|
@ -1,51 +0,0 @@
|
|||
import {
|
||||
listActionsProps,
|
||||
pageListProps,
|
||||
searchPageProps,
|
||||
sortPageProps,
|
||||
tabPageProps,
|
||||
} from "@dashboard/fixtures";
|
||||
import Decorator from "@dashboard/storybook/Decorator";
|
||||
import { PaginatorContextDecorator } from "@dashboard/storybook/PaginatorContextDecorator";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import React from "react";
|
||||
|
||||
import { appsInProgress, appsList } from "../../fixtures";
|
||||
import AppsListPage, { AppsListPageProps } from "./AppsListPage";
|
||||
|
||||
const props: AppsListPageProps = {
|
||||
...listActionsProps,
|
||||
...pageListProps.default,
|
||||
...searchPageProps,
|
||||
...sortPageProps,
|
||||
...tabPageProps,
|
||||
appsInProgressList: {
|
||||
__typename: "Query",
|
||||
appsInstallations: appsInProgress,
|
||||
},
|
||||
disabled: false,
|
||||
installedAppsList: appsList,
|
||||
onAppInProgressRemove: () => undefined,
|
||||
onAppInstallRetry: () => undefined,
|
||||
onSettingsAppOpen: () => undefined,
|
||||
};
|
||||
|
||||
storiesOf("Apps / Apps list", module)
|
||||
.addDecorator(Decorator)
|
||||
.addDecorator(PaginatorContextDecorator)
|
||||
.add("default", () => <AppsListPage {...props} />)
|
||||
.add("loading", () => (
|
||||
<AppsListPage
|
||||
{...props}
|
||||
appsInProgressList={undefined}
|
||||
disabled={true}
|
||||
installedAppsList={[]}
|
||||
/>
|
||||
))
|
||||
.add("no data", () => (
|
||||
<AppsListPage
|
||||
{...props}
|
||||
appsInProgressList={undefined}
|
||||
installedAppsList={[]}
|
||||
/>
|
||||
));
|
|
@ -1,191 +0,0 @@
|
|||
import { AppPageTabs } from "@dashboard/apps/components/AppPageTabs/AppPageTabs";
|
||||
import { useAppsPageNavigation } from "@dashboard/apps/hooks/useAppsPageNavigation";
|
||||
import { useSaleorApps } from "@dashboard/apps/hooks/useSaleorApps";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import {
|
||||
AppListItemFragment,
|
||||
AppsInstallationsQuery,
|
||||
} from "@dashboard/graphql";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { marketplaceUrlResolver } from "@dashboard/marketplace/marketplace-url-resolver";
|
||||
import { ListProps } from "@dashboard/types";
|
||||
import { Button, makeStyles } from "@saleor/macaw-ui";
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import AppsInProgress from "../AppsInProgress/AppsInProgress";
|
||||
import InstalledApps from "../InstalledApps/InstalledApps";
|
||||
|
||||
export interface AppsListPageProps extends ListProps {
|
||||
installedAppsList: AppListItemFragment[];
|
||||
appsInProgressList?: AppsInstallationsQuery;
|
||||
onSettingsAppOpen: (id: string) => void;
|
||||
onAppInProgressRemove: (id: string) => void;
|
||||
onAppInstallRetry: (id: string) => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
topTabs: {
|
||||
marginBottom: theme.spacing(4),
|
||||
},
|
||||
browseMarketplaceContainer: {
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: "AppsListPageStyles",
|
||||
},
|
||||
);
|
||||
|
||||
const AppsListPage: React.FC<AppsListPageProps> = ({
|
||||
appsInProgressList,
|
||||
installedAppsList,
|
||||
onSettingsAppOpen,
|
||||
onAppInProgressRemove,
|
||||
onAppInstallRetry,
|
||||
...listProps
|
||||
}) => {
|
||||
const {
|
||||
fetchApps,
|
||||
apps: fetchedSaleorApps,
|
||||
saleorAppsEnabled,
|
||||
} = useSaleorApps();
|
||||
|
||||
const { updatePath, activeTab } = useAppsPageNavigation();
|
||||
|
||||
useEffect(() => {
|
||||
if (saleorAppsEnabled) {
|
||||
fetchApps();
|
||||
}
|
||||
}, [saleorAppsEnabled, fetchApps]);
|
||||
|
||||
const styles = useStyles();
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigator();
|
||||
|
||||
const appsInProgress = appsInProgressList?.appsInstallations;
|
||||
|
||||
const thirdPartyApps = useMemo(
|
||||
() =>
|
||||
installedAppsList?.filter(
|
||||
app =>
|
||||
!(fetchedSaleorApps ?? []).find(fetchedApp =>
|
||||
app.manifestUrl?.includes(fetchedApp.hostname),
|
||||
),
|
||||
),
|
||||
[installedAppsList, fetchedSaleorApps],
|
||||
);
|
||||
|
||||
const saleorApps = useMemo<AppListItemFragment[]>(
|
||||
() =>
|
||||
(fetchedSaleorApps || []).reduce<AppListItemFragment[]>((acc, app) => {
|
||||
const foundedApp = installedAppsList?.find(installedApp =>
|
||||
installedApp.manifestUrl?.includes(app.hostname),
|
||||
);
|
||||
|
||||
if (foundedApp) {
|
||||
acc.push(foundedApp);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []),
|
||||
[installedAppsList, fetchedSaleorApps],
|
||||
);
|
||||
|
||||
const renderContent = () => {
|
||||
switch (activeTab) {
|
||||
case "third-party": {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Third party apps are installed with App Manifests. They contain UI
|
||||
accessible from dashboard and can extend it."
|
||||
id="MYA6EV"
|
||||
/>
|
||||
</p>
|
||||
{!!appsInProgress?.length && (
|
||||
<>
|
||||
<AppsInProgress
|
||||
appsList={appsInProgress}
|
||||
onAppInstallRetry={onAppInstallRetry}
|
||||
onRemove={onAppInProgressRemove}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</>
|
||||
)}
|
||||
<InstalledApps
|
||||
title={intl.formatMessage({
|
||||
id: "BvmnJq",
|
||||
defaultMessage: "Third Party Apps",
|
||||
description: "section header",
|
||||
})}
|
||||
appsList={thirdPartyApps}
|
||||
onSettingsClick={onSettingsAppOpen}
|
||||
displayQuickManifestButton
|
||||
{...listProps}
|
||||
/>
|
||||
|
||||
<CardSpacer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "saleor-apps": {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Saleor apps are hosted and maintained by Saleor Team. They are
|
||||
preinstalled for you and ready to use"
|
||||
id="FLtdaw"
|
||||
/>
|
||||
</p>
|
||||
<InstalledApps
|
||||
title={intl.formatMessage({
|
||||
id: "PbQJY5",
|
||||
defaultMessage: "Saleor Apps",
|
||||
description: "section header",
|
||||
})}
|
||||
appsList={saleorApps}
|
||||
onSettingsClick={onSettingsAppOpen}
|
||||
{...listProps}
|
||||
/>
|
||||
<div className={styles.browseMarketplaceContainer}>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
navigate(marketplaceUrlResolver.getSaleorAppsDashboardPath());
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Browse Marketplace"
|
||||
id="u0VQMN"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNav title={intl.formatMessage(sectionNames.apps)} />
|
||||
<AppPageTabs
|
||||
showSaleorApps={saleorAppsEnabled}
|
||||
className={styles.topTabs}
|
||||
onChange={updatePath}
|
||||
value={activeTab}
|
||||
/>
|
||||
{renderContent()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
AppsListPage.displayName = "AppsListPage";
|
||||
export default AppsListPage;
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./AppsListPage";
|
||||
export { default } from "./AppsListPage";
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./AppsSkeleton";
|
||||
export { default } from "./AppsSkeleton";
|
|
@ -1,28 +0,0 @@
|
|||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
import { Card, CardContent } from "@material-ui/core";
|
||||
import React from "react";
|
||||
|
||||
import { useStyles } from "../../styles";
|
||||
|
||||
export interface CardContainerProps {
|
||||
children: React.ReactNode;
|
||||
header: React.ReactNode;
|
||||
}
|
||||
|
||||
const CardContainer: React.FC<CardContainerProps> = ({ children, header }) => {
|
||||
const classes = useStyles({});
|
||||
|
||||
return (
|
||||
<div className={classes.appContainer}>
|
||||
<Card>
|
||||
{header}
|
||||
<CardContent className={classes.appContent}>
|
||||
<ResponsiveTable>{children}</ResponsiveTable>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CardContainer.displayName = "CardContainer";
|
||||
export default CardContainer;
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./CardContainer";
|
||||
export { default } from "./CardContainer";
|
|
@ -1,97 +0,0 @@
|
|||
import { Button } from "@dashboard/components/Button";
|
||||
import { TextField } from "@material-ui/core";
|
||||
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>(AvailableStates.Initial);
|
||||
|
||||
const handleFormSubmit: React.FormEventHandler<HTMLFormElement> = 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 (
|
||||
<Button
|
||||
variant="secondary"
|
||||
data-test-id="add-app-from-manifest"
|
||||
onClick={() => setState(AvailableStates.InputOpen)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="kIXV5V"
|
||||
defaultMessage="Install with App Manifest"
|
||||
description="install with app manifest button"
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
case AvailableStates.InputOpen: {
|
||||
return (
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<TextField
|
||||
required
|
||||
type="url"
|
||||
name="manifest-url"
|
||||
label={intl.formatMessage({
|
||||
id: "QVraQY",
|
||||
defaultMessage: "App manifest URL",
|
||||
})}
|
||||
defaultValue=""
|
||||
helperText={intl.formatMessage({
|
||||
id: "o/q4fc",
|
||||
defaultMessage: "Usually ends with /api/manifest",
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
size="medium"
|
||||
type="submit"
|
||||
className={styles.installButton}
|
||||
variant="primary"
|
||||
>
|
||||
{intl.formatMessage({
|
||||
id: "ubmFc8",
|
||||
defaultMessage: "Install",
|
||||
})}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
export * from "./InstallWithManifestFormButton";
|
|
@ -1,125 +0,0 @@
|
|||
import { AppManifestTableDisplay } from "@dashboard/apps/components/AppManifestTableDisplay/AppManifestTableDisplay";
|
||||
import { InstallWithManifestFormButton } from "@dashboard/apps/components/InstallWithManifestFormButton";
|
||||
import { appUrl, createAppInstallUrl } from "@dashboard/apps/urls";
|
||||
import { isAppInTunnel } from "@dashboard/apps/utils";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import { IconButton } from "@dashboard/components/IconButton";
|
||||
import { TableButtonWrapper } from "@dashboard/components/TableButtonWrapper/TableButtonWrapper";
|
||||
import TableRowLink from "@dashboard/components/TableRowLink";
|
||||
import { AppListItemFragment } from "@dashboard/graphql";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { renderCollection } from "@dashboard/misc";
|
||||
import { ListProps } from "@dashboard/types";
|
||||
import { Card, TableBody, TableCell, Typography } from "@material-ui/core";
|
||||
import { ResponsiveTable, SettingsIcon } from "@saleor/macaw-ui";
|
||||
import React, { useCallback } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { useStyles } from "../../styles";
|
||||
import { AppPermissions } from "../AppPermissions/AppPermissions";
|
||||
import AppsSkeleton from "../AppsSkeleton";
|
||||
|
||||
export interface InstalledAppsProps extends ListProps {
|
||||
appsList: AppListItemFragment[];
|
||||
onSettingsClick: (id: string) => void;
|
||||
displayQuickManifestButton?: boolean;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const InstalledApps: React.FC<InstalledAppsProps> = ({
|
||||
appsList,
|
||||
onSettingsClick,
|
||||
title,
|
||||
displayQuickManifestButton = false,
|
||||
...props
|
||||
}) => {
|
||||
const classes = useStyles(props);
|
||||
const navigate = useNavigator();
|
||||
|
||||
const navigateToAppInstallPage = useCallback(
|
||||
(url: string) => {
|
||||
navigate(createAppInstallUrl(url));
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
|
||||
return (
|
||||
<Card className={classes.apps}>
|
||||
<CardTitle
|
||||
title={title}
|
||||
toolbar={
|
||||
displayQuickManifestButton ? (
|
||||
<InstallWithManifestFormButton
|
||||
onSubmitted={navigateToAppInstallPage}
|
||||
/>
|
||||
) : (
|
||||
undefined
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ResponsiveTable>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
appsList,
|
||||
(app, index) =>
|
||||
app ? (
|
||||
<TableRowLink
|
||||
key={app.id}
|
||||
className={classes.tableRow}
|
||||
href={appUrl(app.id)}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<span data-tc="name" className={classes.appName}>
|
||||
{app.name}
|
||||
</span>
|
||||
{app.manifestUrl && isAppInTunnel(app.manifestUrl) ? (
|
||||
<Typography variant="caption">
|
||||
<FormattedMessage
|
||||
defaultMessage="(TUNNEL - DEVELOPMENT)"
|
||||
id="QdQ9z7"
|
||||
/>
|
||||
</Typography>
|
||||
) : null}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className={classes.colAction}>
|
||||
{app.manifestUrl && (
|
||||
<AppManifestTableDisplay manifestUrl={app.manifestUrl} />
|
||||
)}
|
||||
<AppPermissions permissions={app.permissions || []} />
|
||||
<TableButtonWrapper>
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
color="primary"
|
||||
onClick={() => onSettingsClick(app.id)}
|
||||
>
|
||||
<SettingsIcon />
|
||||
</IconButton>
|
||||
</TableButtonWrapper>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
) : (
|
||||
<AppsSkeleton key={index} />
|
||||
),
|
||||
() => (
|
||||
<TableRowLink className={classes.tableRow}>
|
||||
<TableCell className={classes.colName}>
|
||||
<Typography className={classes.text} variant="body2">
|
||||
<FormattedMessage
|
||||
id="9tgY4G"
|
||||
defaultMessage="You don’t have any installed apps in your dashboard"
|
||||
description="apps content"
|
||||
/>
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
InstalledApps.displayName = "InstalledApps";
|
||||
export default InstalledApps;
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./InstalledApps";
|
||||
export { default } from "./InstalledApps";
|
|
@ -1,167 +0,0 @@
|
|||
import {
|
||||
AppFetchMutation,
|
||||
AppListItemFragment,
|
||||
AppQuery,
|
||||
AppsInstallationsQuery,
|
||||
AppTypeEnum,
|
||||
JobStatusEnum,
|
||||
PermissionEnum,
|
||||
} from "@dashboard/graphql";
|
||||
|
||||
export const appsList: AppListItemFragment[] = [
|
||||
{
|
||||
__typename: "App",
|
||||
id: "QXBwOjE3Ng==",
|
||||
isActive: true,
|
||||
name: "app",
|
||||
type: AppTypeEnum.THIRDPARTY,
|
||||
version: "1.0.0",
|
||||
appUrl: null,
|
||||
manifestUrl: "http://localhost:3000/api/manifest",
|
||||
permissions: [
|
||||
{
|
||||
__typename: "Permission",
|
||||
code: PermissionEnum.MANAGE_USERS,
|
||||
name: "Manage customers.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
__typename: "App",
|
||||
id: "QXBwOjE3Ng==",
|
||||
isActive: false,
|
||||
name: "app1",
|
||||
type: AppTypeEnum.THIRDPARTY,
|
||||
version: "1.0.0",
|
||||
appUrl: "http://localhost:3000",
|
||||
manifestUrl: "http://localhost:3000/api/manifest",
|
||||
permissions: [
|
||||
{
|
||||
__typename: "Permission",
|
||||
code: PermissionEnum.MANAGE_ORDERS,
|
||||
name: "Manage orders.",
|
||||
},
|
||||
{
|
||||
__typename: "Permission",
|
||||
code: PermissionEnum.MANAGE_USERS,
|
||||
name: "Manage customers.",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const customAppsList: AppListItemFragment[] = [
|
||||
{
|
||||
__typename: "App",
|
||||
id: "QXBwOjE3Ng==",
|
||||
isActive: true,
|
||||
name: "app custom",
|
||||
type: AppTypeEnum.LOCAL,
|
||||
version: "1.0.0",
|
||||
appUrl: null,
|
||||
manifestUrl: null,
|
||||
permissions: [
|
||||
{
|
||||
__typename: "Permission",
|
||||
code: PermissionEnum.MANAGE_ORDERS,
|
||||
name: "Manage orders.",
|
||||
},
|
||||
{
|
||||
__typename: "Permission",
|
||||
code: PermissionEnum.MANAGE_USERS,
|
||||
name: "Manage customers.",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const appsInProgress: AppsInstallationsQuery["appsInstallations"] = [
|
||||
{
|
||||
__typename: "AppInstallation",
|
||||
appName: "app",
|
||||
id: "QXBwSW5zdGFsbGF0aW9uOjk2",
|
||||
manifestUrl: "http://localhost:3000/manifest",
|
||||
message: "Failed to connect to app. Try later or contact with app support.",
|
||||
status: JobStatusEnum.FAILED,
|
||||
},
|
||||
{
|
||||
__typename: "AppInstallation",
|
||||
appName: "app pending",
|
||||
id: "QXBwSW5zdGFsbGF0aW9uOjk2",
|
||||
manifestUrl: "http://localhost:3000/manifest",
|
||||
message: "Pending.",
|
||||
status: JobStatusEnum.PENDING,
|
||||
},
|
||||
{
|
||||
__typename: "AppInstallation",
|
||||
appName: "app success",
|
||||
id: "QXBwSW5zdGFsbGF0aW9uOjk2",
|
||||
manifestUrl: "http://localhost:3000/manifest",
|
||||
message: "Success.",
|
||||
status: JobStatusEnum.SUCCESS,
|
||||
},
|
||||
];
|
||||
|
||||
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: NonNullable<
|
||||
AppFetchMutation["appFetchManifest"]
|
||||
>["manifest"] = {
|
||||
__typename: "Manifest",
|
||||
about: "Lorem ipsum",
|
||||
appUrl: null,
|
||||
configurationUrl: null,
|
||||
dataPrivacy: null,
|
||||
dataPrivacyUrl: null,
|
||||
homepageUrl: null,
|
||||
identifier: "app",
|
||||
name: "app",
|
||||
permissions: [
|
||||
{
|
||||
__typename: "Permission",
|
||||
code: PermissionEnum.MANAGE_USERS,
|
||||
name: "Manage users",
|
||||
},
|
||||
{
|
||||
__typename: "Permission",
|
||||
code: PermissionEnum.MANAGE_ORDERS,
|
||||
name: "Manage orders",
|
||||
},
|
||||
],
|
||||
supportUrl: null,
|
||||
tokenTargetUrl: null,
|
||||
version: "1.0",
|
||||
};
|
|
@ -1,40 +0,0 @@
|
|||
import { appsSection } from "@dashboard/apps/urls";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import useRouter from "use-react-router";
|
||||
|
||||
export type AppPagePathSegment = "third-party" | "saleor-apps";
|
||||
|
||||
const defaultTab: AppPagePathSegment = "third-party";
|
||||
const appTypeQueryParam = "type";
|
||||
|
||||
export const useAppsPageNavigation = () => {
|
||||
const navigate = useNavigator();
|
||||
const {
|
||||
location: { search },
|
||||
} = useRouter();
|
||||
|
||||
const updatePath = useCallback(
|
||||
(value: AppPagePathSegment) => {
|
||||
const qs = new URLSearchParams({
|
||||
[appTypeQueryParam]: value,
|
||||
}).toString();
|
||||
|
||||
navigate(`${appsSection}?${qs}`, { replace: true, resetScroll: true });
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
|
||||
const activeTab: AppPagePathSegment = useMemo(
|
||||
() =>
|
||||
(new URLSearchParams(search).get(
|
||||
appTypeQueryParam,
|
||||
) as AppPagePathSegment) ?? defaultTab,
|
||||
[search],
|
||||
);
|
||||
|
||||
return {
|
||||
updatePath,
|
||||
activeTab,
|
||||
};
|
||||
};
|
|
@ -1,44 +0,0 @@
|
|||
import { marketplaceUrlResolver } from "@dashboard/marketplace/marketplace-url-resolver";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export interface SaleorApp {
|
||||
name: string;
|
||||
hostname: string;
|
||||
}
|
||||
|
||||
const saleorAppsEnabled = marketplaceUrlResolver.checkMarketplaceConfigExists();
|
||||
|
||||
export const useSaleorApps = () => {
|
||||
const [apps, setApps] = useState<SaleorApp[] | undefined>(undefined);
|
||||
|
||||
const fetchApps = useCallback(async () => {
|
||||
if (!saleorAppsEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
return fetch(marketplaceUrlResolver.getSaleorAppsJsonEndpoint())
|
||||
.then(response => response.json())
|
||||
.then((data: SaleorApp[]) => {
|
||||
if (
|
||||
!data.every(
|
||||
item =>
|
||||
typeof item.hostname === "string" ||
|
||||
typeof item.name === "string",
|
||||
)
|
||||
) {
|
||||
console.error(
|
||||
'Invalid Saleor Apps data received from Marketplace. Expected array of objects with "name" and "hostname" properties',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setApps(data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return {
|
||||
saleorAppsEnabled,
|
||||
apps,
|
||||
fetchApps,
|
||||
};
|
||||
};
|
|
@ -1,32 +0,0 @@
|
|||
import WebhooksRoutes from "@dashboard/custom-apps";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { parse as parseQs } from "qs";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { Route, RouteComponentProps, Switch } from "react-router-dom";
|
||||
|
||||
import { WindowTitle } from "../components/WindowTitle";
|
||||
import { AppListUrlQueryParams, appsListPath } from "./urls";
|
||||
import AppsListView from "./views/AppsList";
|
||||
|
||||
const AppsList: React.FC<RouteComponentProps> = () => {
|
||||
const qs = parseQs(location.search.substr(1));
|
||||
const params: AppListUrlQueryParams = qs;
|
||||
|
||||
return <AppsListView params={params} />;
|
||||
};
|
||||
const Component = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<>
|
||||
<WindowTitle title={intl.formatMessage(sectionNames.apps)} />
|
||||
<Switch>
|
||||
<Route exact path={appsListPath} component={AppsList} />
|
||||
<WebhooksRoutes />
|
||||
</Switch>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Component;
|
|
@ -1,220 +0,0 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
[theme.breakpoints.up("lg")]: {
|
||||
colName: {
|
||||
paddingLeft: "0 !important",
|
||||
"&&": {
|
||||
width: "auto",
|
||||
},
|
||||
},
|
||||
},
|
||||
alignRight: {
|
||||
textAlign: "right",
|
||||
},
|
||||
apps: {
|
||||
marginBottom: theme.spacing(4),
|
||||
},
|
||||
appContent: {
|
||||
"&:last-child": {
|
||||
padding: "0!important",
|
||||
},
|
||||
padding: 0,
|
||||
},
|
||||
appHeader: {
|
||||
marginBottom: theme.spacing(3),
|
||||
},
|
||||
appHeaderLinks: {
|
||||
"& img": {
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
padding: theme.spacing(2, 0),
|
||||
},
|
||||
appName: {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
colAction: {
|
||||
"&&": {
|
||||
paddingRight: theme.spacing(3),
|
||||
textAlign: "right",
|
||||
},
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
textAlign: "right",
|
||||
gap: theme.spacing(1),
|
||||
},
|
||||
colInstallAction: {
|
||||
"& > *": {
|
||||
display: "inline-flex",
|
||||
},
|
||||
},
|
||||
colName: {
|
||||
paddingLeft: 0,
|
||||
minWidth: theme.spacing(30),
|
||||
},
|
||||
colSpinner: {
|
||||
"& svg": {
|
||||
textAlign: "right",
|
||||
},
|
||||
paddingLeft: theme.spacing(3),
|
||||
paddingRight: theme.spacing(2),
|
||||
},
|
||||
customApps: {
|
||||
marginBottom: theme.spacing(4),
|
||||
},
|
||||
customTooltip: {
|
||||
"& > div": {
|
||||
backgroundColor: theme.palette.error.main,
|
||||
borderRadius: theme.spacing(1),
|
||||
color: theme.palette.primary.contrastText,
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
padding: "0!important",
|
||||
},
|
||||
error: {
|
||||
"& button": {
|
||||
marginLeft: theme.spacing(0.6),
|
||||
},
|
||||
color: theme.palette.error.main,
|
||||
marginRight: theme.spacing(1),
|
||||
alignItems: "flex-end",
|
||||
},
|
||||
headerLinkContainer: {
|
||||
"& svg": {
|
||||
marginRight: theme.spacing(),
|
||||
},
|
||||
"& span": {
|
||||
fontWeight: 500,
|
||||
},
|
||||
alignItems: "center",
|
||||
color: theme.palette.text.primary,
|
||||
display: "flex",
|
||||
fontSize: theme.spacing(2),
|
||||
fontWeight: 500,
|
||||
lineHeight: 1.2,
|
||||
marginRight: theme.spacing(3),
|
||||
padding: 0,
|
||||
textTransform: "none",
|
||||
},
|
||||
hr: {
|
||||
border: "none",
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
height: 0,
|
||||
marginBottom: 0,
|
||||
marginTop: 0,
|
||||
width: "100%",
|
||||
},
|
||||
installAppContainer: {
|
||||
"& > div": {
|
||||
position: "relative",
|
||||
},
|
||||
"& img": {
|
||||
position: "relative",
|
||||
},
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
padding: theme.spacing(2, 0),
|
||||
position: "relative",
|
||||
width: theme.spacing(35),
|
||||
"&:before": {
|
||||
backgroundColor: theme.palette.divider,
|
||||
content: "''",
|
||||
height: 2,
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
transform: "translateY(-50%)",
|
||||
width: theme.spacing(30),
|
||||
},
|
||||
},
|
||||
installCard: {
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
position: "relative",
|
||||
},
|
||||
installIcon: {
|
||||
alignItems: "center",
|
||||
backgroundColor: theme.palette.divider,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
borderRadius: "50%",
|
||||
display: "flex",
|
||||
height: theme.spacing(9),
|
||||
justifyContent: "center",
|
||||
overflow: "hidden",
|
||||
width: theme.spacing(9),
|
||||
},
|
||||
installPermissionTitle: {
|
||||
fontWeight: 500,
|
||||
},
|
||||
installPrivacyText: {
|
||||
"& a": {
|
||||
color: theme.palette.primary.main,
|
||||
textDecoration: "none",
|
||||
},
|
||||
color: theme.palette.text.hint,
|
||||
marginTop: theme.spacing(1),
|
||||
},
|
||||
installSaleorIcon: {
|
||||
backgroundColor:
|
||||
theme.palette.type === "dark"
|
||||
? theme.palette.saleor.gray.default
|
||||
: theme.palette.saleor.main[1],
|
||||
border: "none",
|
||||
},
|
||||
installSpacer: {
|
||||
margin: theme.spacing(2, 0),
|
||||
},
|
||||
installText: {
|
||||
color: theme.palette.primary.contrastText,
|
||||
},
|
||||
linkContainer: {
|
||||
fontWeight: 500,
|
||||
marginTop: theme.spacing(1.5),
|
||||
},
|
||||
marketplaceContent: {
|
||||
"& button": {
|
||||
marginTop: theme.spacing(1),
|
||||
},
|
||||
"&:last-child": {
|
||||
padding: theme.spacing(2, 3, 2, 3),
|
||||
},
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
permissionsContainer: {
|
||||
"& li": {
|
||||
"&:last-child": {
|
||||
marginBottom: 0,
|
||||
},
|
||||
marginBottom: theme.spacing(1),
|
||||
},
|
||||
paddingLeft: theme.spacing(2),
|
||||
},
|
||||
retryBtnCol: {
|
||||
paddingRight: theme.spacing(1),
|
||||
width: theme.spacing(14),
|
||||
},
|
||||
statusWrapper: {
|
||||
display: "inline-block",
|
||||
marginLeft: theme.spacing(2.5),
|
||||
},
|
||||
table: {
|
||||
tableLayout: "fixed",
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer",
|
||||
},
|
||||
text: {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
activateButton: {
|
||||
"& img": {
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
}),
|
||||
{ name: "AppList" },
|
||||
);
|
|
@ -1,50 +0,0 @@
|
|||
import { resolveAppIframeUrl } from "@dashboard/apps/urls";
|
||||
import * as config from "@dashboard/config";
|
||||
import { ThemeType } from "@saleor/app-sdk/app-bridge";
|
||||
|
||||
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 = 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/",
|
||||
);
|
||||
});
|
153
src/apps/urls.ts
153
src/apps/urls.ts
|
@ -1,153 +0,0 @@
|
|||
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 { ActiveTab, Dialog, Pagination, SingleAction } from "../types";
|
||||
|
||||
export const MANIFEST_ATTR = "manifestUrl";
|
||||
|
||||
export type AppListUrlDialog = "app-installation-remove";
|
||||
|
||||
export type AppDetailsUrlDialog =
|
||||
| "app-activate"
|
||||
| "app-deactivate"
|
||||
| "app-delete";
|
||||
|
||||
export type AppListUrlQueryParams = ActiveTab &
|
||||
Dialog<AppListUrlDialog> &
|
||||
SingleAction &
|
||||
Pagination;
|
||||
|
||||
export interface AppDetailsUrlMountQueryParams {
|
||||
productId?: string;
|
||||
productIds?: string[];
|
||||
orderId?: string;
|
||||
customerId?: string;
|
||||
customerIds?: string[];
|
||||
}
|
||||
|
||||
export interface AppDetailsCommonParams {
|
||||
theme: ThemeType;
|
||||
}
|
||||
|
||||
interface FeatureFlagsQueryParams {
|
||||
featureFlags?: Record<string, string>;
|
||||
}
|
||||
|
||||
export type AppDetailsUrlQueryParams = Dialog<AppDetailsUrlDialog> &
|
||||
SingleAction &
|
||||
AppDetailsUrlMountQueryParams &
|
||||
FeatureFlagsQueryParams;
|
||||
|
||||
export type AppInstallUrlQueryParams = Partial<{ [MANIFEST_ATTR]: string }>;
|
||||
|
||||
export enum AppListUrlSortField {
|
||||
name = "name",
|
||||
active = "active",
|
||||
}
|
||||
|
||||
export const appsSection = "/apps/";
|
||||
export const appsListPath = appsSection;
|
||||
|
||||
export const appDetailsPath = (id: string) => urlJoin(appsSection, id);
|
||||
export const appPath = (id: string) => urlJoin(appsSection, id, "app");
|
||||
export const appDeepPath = (id: string, subPath: string) =>
|
||||
urlJoin(appPath(id), subPath);
|
||||
export const appInstallPath = urlJoin(appsSection, "install");
|
||||
export const createAppInstallUrl = (manifestUrl: string) =>
|
||||
`${appInstallPath}?manifestUrl=${manifestUrl}`;
|
||||
|
||||
export const appDetailsUrl = (id: string, params?: AppDetailsUrlQueryParams) =>
|
||||
appDetailsPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
||||
|
||||
export const appUrl = (id: string, params?: AppDetailsUrlQueryParams) =>
|
||||
appPath(encodeURIComponent(id)) + "?" + stringifyQs(params);
|
||||
export const appDeepUrl = (
|
||||
id: string,
|
||||
subPath: string,
|
||||
params?: AppDetailsUrlQueryParams,
|
||||
) => appDeepPath(encodeURIComponent(id), subPath) + "?" + stringifyQs(params);
|
||||
|
||||
export const getAppDeepPathFromDashboardUrl = (
|
||||
dashboardUrl: string,
|
||||
appId: string,
|
||||
) => {
|
||||
const deepSubPath = dashboardUrl.replace(
|
||||
appPath(encodeURIComponent(appId)),
|
||||
"",
|
||||
);
|
||||
return deepSubPath || "/";
|
||||
};
|
||||
export const getAppCompleteUrlFromDashboardUrl = (
|
||||
dashboardUrl: string,
|
||||
appUrl?: string,
|
||||
appId?: string,
|
||||
) => {
|
||||
if (!appUrl || !appId) {
|
||||
return appUrl;
|
||||
}
|
||||
const deepSubPath = dashboardUrl.replace(
|
||||
appPath(encodeURIComponent(appId)),
|
||||
"",
|
||||
);
|
||||
const appCompleteUrl = urlJoin(appUrl, deepSubPath);
|
||||
return appCompleteUrl;
|
||||
};
|
||||
export const getDashboardUrFromAppCompleteUrl = (
|
||||
appCompleteUrl: string,
|
||||
appUrl?: string,
|
||||
appId?: string,
|
||||
) => {
|
||||
if (!appUrl || !appId) {
|
||||
return appUrl;
|
||||
}
|
||||
const deepSubPath = appCompleteUrl.replace(appUrl, "");
|
||||
const dashboardUrl = urlJoin(appPath(encodeURIComponent(appId)), deepSubPath);
|
||||
return dashboardUrl;
|
||||
};
|
||||
|
||||
export const appsListUrl = (params?: AppListUrlQueryParams) =>
|
||||
appsListPath + "?" + stringifyQs(params);
|
||||
|
||||
export const 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);
|
||||
};
|
||||
|
||||
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;
|
||||
}, {});
|
|
@ -1,11 +0,0 @@
|
|||
import { AppsInstallationsQuery } from "@dashboard/graphql";
|
||||
|
||||
const tunnelKeywords = [".ngrok.io", ".saleor.live"];
|
||||
|
||||
export const isAppInTunnel = (manifestUrl: string) =>
|
||||
Boolean(tunnelKeywords.find(keyword => manifestUrl.includes(keyword)));
|
||||
|
||||
export const getAppInProgressName = (
|
||||
id: string,
|
||||
collection?: AppsInstallationsQuery["appsInstallations"],
|
||||
) => collection?.find(app => app.id === id)?.appName || id;
|
|
@ -1,232 +0,0 @@
|
|||
import { useApolloClient } from "@apollo/client";
|
||||
import { getAppInProgressName } from "@dashboard/apps/utils";
|
||||
import {
|
||||
AppSortField,
|
||||
AppTypeEnum,
|
||||
JobStatusEnum,
|
||||
OrderDirection,
|
||||
useAppDeleteFailedInstallationMutation,
|
||||
useAppRetryInstallMutation,
|
||||
useAppsInstallationsQuery,
|
||||
useAppsListQuery,
|
||||
} from "@dashboard/graphql";
|
||||
import useListSettings from "@dashboard/hooks/useListSettings";
|
||||
import useLocalStorage from "@dashboard/hooks/useLocalStorage";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||
import usePaginator, {
|
||||
createPaginationState,
|
||||
PageInfo,
|
||||
PaginatorContext,
|
||||
} from "@dashboard/hooks/usePaginator";
|
||||
import { ListViews } from "@dashboard/types";
|
||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
||||
import { mapEdgesToItems } from "@dashboard/utils/maps";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { EXTENSION_LIST_QUERY } from "../../../new-apps/queries";
|
||||
import AppInProgressDeleteDialog from "../../components/AppInProgressDeleteDialog";
|
||||
import AppsListPage from "../../components/AppsListPage";
|
||||
import {
|
||||
appDetailsUrl,
|
||||
AppListUrlDialog,
|
||||
AppListUrlQueryParams,
|
||||
appsListUrl,
|
||||
} from "../../urls";
|
||||
import { messages } from "./messages";
|
||||
|
||||
interface AppsListProps {
|
||||
params: AppListUrlQueryParams;
|
||||
}
|
||||
|
||||
export const AppsList: React.FC<AppsListProps> = ({ params }) => {
|
||||
const { action } = params;
|
||||
const client = useApolloClient();
|
||||
const [activeInstallations, setActiveInstallations] = useLocalStorage<
|
||||
Array<Record<"id" | "name", string>>
|
||||
>("activeInstallations", []);
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
const navigate = useNavigator();
|
||||
const { updateListSettings, settings } = useListSettings(ListViews.APPS_LIST);
|
||||
const paginationState = createPaginationState(settings?.rowNumber, params);
|
||||
const queryVariables = {
|
||||
sort: {
|
||||
direction: OrderDirection.DESC,
|
||||
field: AppSortField.CREATION_DATE,
|
||||
},
|
||||
};
|
||||
const intervalId = useRef<null | number>(null);
|
||||
|
||||
const removeInstallation = (id: string) =>
|
||||
setActiveInstallations(installations =>
|
||||
installations.filter(item => item.id !== id),
|
||||
);
|
||||
|
||||
const { data: appsInProgressData, refetch: appsInProgressRefetch } =
|
||||
useAppsInstallationsQuery({
|
||||
displayLoader: false,
|
||||
});
|
||||
const { data, loading, refetch } = useAppsListQuery({
|
||||
displayLoader: true,
|
||||
variables: {
|
||||
...paginationState,
|
||||
...queryVariables,
|
||||
filter: {
|
||||
type: AppTypeEnum.THIRDPARTY,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const paginationValues = usePaginator({
|
||||
pageInfo: data?.apps?.pageInfo as PageInfo,
|
||||
paginationState,
|
||||
queryString: params,
|
||||
});
|
||||
|
||||
const refetchExtensionList = () => {
|
||||
client.refetchQueries({
|
||||
include: [EXTENSION_LIST_QUERY],
|
||||
});
|
||||
};
|
||||
|
||||
const installedAppNotify = (name: string) => {
|
||||
notify({
|
||||
status: "success",
|
||||
text: intl.formatMessage(messages.appReadyToUse, { name }),
|
||||
title: intl.formatMessage(messages.appInstalled),
|
||||
});
|
||||
};
|
||||
const [retryInstallApp] = useAppRetryInstallMutation({
|
||||
onCompleted: data => {
|
||||
if (!data?.appRetryInstall?.errors?.length) {
|
||||
const appInstallation = data.appRetryInstall?.appInstallation;
|
||||
if (appInstallation) {
|
||||
setActiveInstallations(installations => [
|
||||
...installations,
|
||||
{
|
||||
id: appInstallation.id,
|
||||
name: appInstallation.appName,
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
const [openModal, closeModal] = createDialogActionHandlers<
|
||||
AppListUrlDialog,
|
||||
AppListUrlQueryParams
|
||||
>(navigate, appsListUrl, params);
|
||||
|
||||
const [deleteInProgressApp, deleteInProgressAppOpts] =
|
||||
useAppDeleteFailedInstallationMutation({
|
||||
onCompleted: data => {
|
||||
if (!data?.appDeleteFailedInstallation?.errors?.length) {
|
||||
removeAppNotify();
|
||||
appsInProgressRefetch();
|
||||
closeModal();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const appsInProgress = appsInProgressData?.appsInstallations || [];
|
||||
if (activeInstallations.length && !!appsInProgressData) {
|
||||
if (!intervalId.current) {
|
||||
intervalId.current = window.setInterval(
|
||||
() => appsInProgressRefetch(),
|
||||
2000,
|
||||
);
|
||||
}
|
||||
let newAppInstalled = false;
|
||||
activeInstallations.forEach(installation => {
|
||||
const item = appsInProgress?.find(app => app.id === installation.id);
|
||||
if (!item) {
|
||||
removeInstallation(installation.id);
|
||||
installedAppNotify(installation.name);
|
||||
appsInProgressRefetch();
|
||||
newAppInstalled = true;
|
||||
} else if (item.status === JobStatusEnum.SUCCESS) {
|
||||
removeInstallation(installation.id);
|
||||
installedAppNotify(item.appName);
|
||||
refetch();
|
||||
newAppInstalled = true;
|
||||
} else if (item.status === JobStatusEnum.FAILED) {
|
||||
removeInstallation(installation.id);
|
||||
notify({
|
||||
status: "error",
|
||||
text: item.message,
|
||||
title: intl.formatMessage(messages.appCouldntInstall, {
|
||||
name: item.appName,
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
||||
if (newAppInstalled) {
|
||||
refetchExtensionList();
|
||||
}
|
||||
}
|
||||
if (!activeInstallations.length && intervalId.current) {
|
||||
clearInterval(intervalId.current);
|
||||
intervalId.current = null;
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (intervalId.current) {
|
||||
clearInterval(intervalId.current);
|
||||
intervalId.current = null;
|
||||
}
|
||||
};
|
||||
}, [activeInstallations.length, appsInProgressData]);
|
||||
|
||||
const handleRemoveInProgressConfirm = () =>
|
||||
deleteInProgressApp({
|
||||
variables: {
|
||||
id: params?.id || "",
|
||||
},
|
||||
});
|
||||
|
||||
const removeAppNotify = () => {
|
||||
notify({
|
||||
status: "success",
|
||||
text: intl.formatMessage(messages.appRemoved),
|
||||
});
|
||||
};
|
||||
|
||||
const onAppInstallRetry = (id: string) =>
|
||||
retryInstallApp({ variables: { id } });
|
||||
|
||||
const installedApps = mapEdgesToItems(data?.apps || { edges: [] }) || [];
|
||||
|
||||
return (
|
||||
<PaginatorContext.Provider value={paginationValues}>
|
||||
<AppInProgressDeleteDialog
|
||||
confirmButtonState={deleteInProgressAppOpts.status}
|
||||
name={getAppInProgressName(
|
||||
params.id || "",
|
||||
appsInProgressData?.appsInstallations,
|
||||
)}
|
||||
onClose={closeModal}
|
||||
onConfirm={handleRemoveInProgressConfirm}
|
||||
open={action === "app-installation-remove"}
|
||||
/>
|
||||
<AppsListPage
|
||||
installedAppsList={installedApps}
|
||||
appsInProgressList={appsInProgressData}
|
||||
disabled={loading}
|
||||
settings={settings}
|
||||
onUpdateListSettings={updateListSettings}
|
||||
onAppInstallRetry={onAppInstallRetry}
|
||||
onSettingsAppOpen={id => navigate(appDetailsUrl(id))}
|
||||
onAppInProgressRemove={id =>
|
||||
openModal("app-installation-remove", {
|
||||
id,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</PaginatorContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppsList;
|
|
@ -1,2 +0,0 @@
|
|||
export * from "./AppsList";
|
||||
export { default } from "./AppsList";
|
|
@ -1,34 +0,0 @@
|
|||
import { defineMessages } from "react-intl";
|
||||
|
||||
export const messages = defineMessages({
|
||||
appInstalled: {
|
||||
id: "0fM/pV",
|
||||
defaultMessage: "App installed",
|
||||
description: "message title",
|
||||
},
|
||||
appRemoved: {
|
||||
id: "uIPD1i",
|
||||
defaultMessage: "App successfully removed",
|
||||
description: "app has been removed",
|
||||
},
|
||||
appReadyToUse: {
|
||||
id: "ZprV2g",
|
||||
defaultMessage: "{name} is ready to be used",
|
||||
description: "app has been installed",
|
||||
},
|
||||
appCouldntInstall: {
|
||||
id: "5t/4um",
|
||||
defaultMessage: "Couldn’t Install {name}",
|
||||
description: "message title",
|
||||
},
|
||||
appActivated: {
|
||||
id: "D/+84n",
|
||||
defaultMessage: "App activated",
|
||||
description: "snackbar text",
|
||||
},
|
||||
appDeactivated: {
|
||||
id: "USO8PB",
|
||||
defaultMessage: "App deactivated",
|
||||
description: "snackbar text",
|
||||
},
|
||||
});
|
|
@ -1,9 +1,9 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import { inputTypeMessages } from "@dashboard/attributes/components/AttributeDetails/messages";
|
||||
import { AttributeValueEditDialogFormData } from "@dashboard/attributes/utils/data";
|
||||
import { ColorPicker } from "@dashboard/components/ColorPicker";
|
||||
import FileUploadField from "@dashboard/components/FileUploadField";
|
||||
import { RadioGroupField } from "@dashboard/components/RadioGroupField";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { useFileUploadMutation } from "@dashboard/graphql";
|
||||
import { UseFormResult } from "@dashboard/hooks/useForm";
|
||||
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||
|
@ -21,9 +21,9 @@ type AttributeSwatchFieldProps<T> = Pick<
|
|||
|
||||
type SwatchType = "picker" | "image";
|
||||
|
||||
const AttributeSwatchField: React.FC<AttributeSwatchFieldProps<
|
||||
AttributeValueEditDialogFormData
|
||||
>> = ({ set, ...props }) => {
|
||||
const AttributeSwatchField: React.FC<
|
||||
AttributeSwatchFieldProps<AttributeValueEditDialogFormData>
|
||||
> = ({ set, ...props }) => {
|
||||
const { data } = props;
|
||||
const notify = useNotifier();
|
||||
const intl = useIntl();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import { InternalLink } from "@dashboard/components/InternalLink";
|
||||
import { CategoryDetailsQuery } from "@dashboard/graphql";
|
||||
import { productAddUrl, productListUrl } from "@dashboard/products/urls";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import { AccordionSummary } from "@saleor/macaw-ui";
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import BasicAttributeRow from "@dashboard/components/Attributes/BasicAttributeRow";
|
||||
import {
|
||||
getErrorMessage,
|
||||
getSingleDisplayValue,
|
||||
} from "@dashboard/components/Attributes/utils";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import SingleAutocompleteSelectField from "@dashboard/components/SingleAutocompleteSelectField";
|
||||
import { getBySlug } from "@dashboard/misc";
|
||||
import { InputAdornment } from "@material-ui/core";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import { CollectionFragment } from "@dashboard/graphql";
|
||||
import ScrollableContent from "@dashboard/plugins/components/PluginsList/PluginAvailabilityStatusPopup/ScrollableContent";
|
||||
import { Typography } from "@material-ui/core";
|
||||
|
@ -18,9 +18,9 @@ export interface Pill {
|
|||
label: MessageDescriptor;
|
||||
}
|
||||
|
||||
export const ChannelsAvailabilityMenuContent: React.FC<ChannelsAvailabilityMenuContentProps> = ({
|
||||
pills,
|
||||
}) => {
|
||||
export const ChannelsAvailabilityMenuContent: React.FC<
|
||||
ChannelsAvailabilityMenuContentProps
|
||||
> = ({ pills }) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles({});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import { UseFormResult } from "@dashboard/hooks/useForm";
|
||||
import { RequireOnlyOne } from "@dashboard/misc";
|
||||
import commonErrorMessages from "@dashboard/utils/errors/common";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import { Button, Dialog, DialogContent, makeStyles } from "@material-ui/core";
|
||||
import { DialogHeader } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import chevronDown from "@assets/images/ChevronDown.svg";
|
||||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import Checkbox from "@dashboard/components/Checkbox";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import useElementScroll, {
|
||||
isScrolledToBottom,
|
||||
} from "@dashboard/hooks/useElementScroll";
|
||||
|
@ -165,7 +165,9 @@ function getChoiceIndex(
|
|||
return choiceIndex;
|
||||
}
|
||||
|
||||
const MultiAutocompleteSelectFieldContent: React.FC<MultiAutocompleteSelectFieldContentProps> = props => {
|
||||
const MultiAutocompleteSelectFieldContent: React.FC<
|
||||
MultiAutocompleteSelectFieldContentProps
|
||||
> = props => {
|
||||
const {
|
||||
add,
|
||||
choices = [],
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { appsListUrl } from "@dashboard/apps/urls";
|
||||
import { attributeListUrl } from "@dashboard/attributes/urls";
|
||||
import { categoryListUrl } from "@dashboard/categories/urls";
|
||||
import { collectionListUrl } from "@dashboard/collections/urls";
|
||||
|
@ -7,6 +6,7 @@ import { saleListUrl, voucherListUrl } from "@dashboard/discounts/urls";
|
|||
import { UseNavigatorResult } from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { menuListUrl } from "@dashboard/navigation/urls";
|
||||
import { AppUrls } from "@dashboard/new-apps/urls";
|
||||
import { orderDraftListUrl, orderListUrl } from "@dashboard/orders/urls";
|
||||
import { pageListUrl } from "@dashboard/pages/urls";
|
||||
import { permissionGroupListUrl } from "@dashboard/permissionGroups/urls";
|
||||
|
@ -36,7 +36,7 @@ function searchInViews(
|
|||
const views: View[] = [
|
||||
{
|
||||
label: intl.formatMessage(sectionNames.apps),
|
||||
url: appsListUrl(),
|
||||
url: AppUrls.resolveAppListUrl(),
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage(sectionNames.attributes),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { appsListPath } from "@dashboard/apps/urls";
|
||||
import { useUser } from "@dashboard/auth";
|
||||
import { categoryListUrl } from "@dashboard/categories/urls";
|
||||
import { collectionListUrl } from "@dashboard/collections/urls";
|
||||
|
@ -15,6 +14,7 @@ import {
|
|||
extensionMountPoints,
|
||||
useExtensions,
|
||||
} from "@dashboard/new-apps/hooks/useExtensions";
|
||||
import { AppPaths } from "@dashboard/new-apps/urls";
|
||||
import { orderDraftListUrl, orderListUrl } from "@dashboard/orders/urls";
|
||||
import { pageListPath } from "@dashboard/pages/urls";
|
||||
import { productListUrl } from "@dashboard/products/urls";
|
||||
|
@ -68,7 +68,7 @@ export function useMenuStructure() {
|
|||
{
|
||||
label: intl.formatMessage(sectionNames.apps),
|
||||
id: "apps",
|
||||
url: appsListPath,
|
||||
url: AppPaths.appListPath,
|
||||
type: "item",
|
||||
},
|
||||
{
|
||||
|
@ -92,7 +92,7 @@ export function useMenuStructure() {
|
|||
label: intl.formatMessage(sectionNames.apps),
|
||||
permissions: [PermissionEnum.MANAGE_APPS],
|
||||
id: "apps",
|
||||
url: appsListPath,
|
||||
url: AppPaths.appListPath,
|
||||
type: "item",
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { getDashboardUrFromAppCompleteUrl } from "@dashboard/apps/urls";
|
||||
import { AppExtensionMountEnum } from "@dashboard/graphql";
|
||||
import { Extension } from "@dashboard/new-apps/hooks/useExtensions";
|
||||
import { AppUrls } from "@dashboard/new-apps/urls";
|
||||
import { orderDraftListUrl, orderListUrl } from "@dashboard/orders/urls";
|
||||
import { matchPath } from "react-router";
|
||||
|
||||
|
@ -14,7 +14,11 @@ export const mapToExtensionsItems = (
|
|||
({ label, id, app, url, permissions, open }) => ({
|
||||
id: `extension-${id}`,
|
||||
label,
|
||||
url: getDashboardUrFromAppCompleteUrl(url, app.appUrl, app.id),
|
||||
url: AppUrls.resolveDashboardUrlFromAppCompleteUrl(
|
||||
url,
|
||||
app.appUrl,
|
||||
app.id,
|
||||
),
|
||||
permissions,
|
||||
onClick: open,
|
||||
type: "item",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import ConfirmButton from "@dashboard/components/ConfirmButton";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { CardContent } from "@material-ui/core";
|
||||
import React, { useState } from "react";
|
||||
|
@ -23,7 +23,9 @@ interface TypeDeleteWarningDialogContentProps {
|
|||
showViewAssignedItemsButton?: boolean;
|
||||
}
|
||||
|
||||
const TypeDeleteWarningDialogContent: React.FC<TypeDeleteWarningDialogContentProps> = ({
|
||||
const TypeDeleteWarningDialogContent: React.FC<
|
||||
TypeDeleteWarningDialogContentProps
|
||||
> = ({
|
||||
description,
|
||||
consentLabel,
|
||||
viewAssignedItemsUrl,
|
||||
|
|
|
@ -7,15 +7,15 @@ import { CustomAppUrls } from "@dashboard/custom-apps/urls";
|
|||
import { AppListItemFragment } from "@dashboard/graphql";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { renderCollection } from "@dashboard/misc";
|
||||
import DeactivatedText from "@dashboard/new-apps/components/DeactivatedText";
|
||||
import { TableBody, TableCell, Typography } from "@material-ui/core";
|
||||
import { DeleteIcon, IconButton, ResponsiveTable } from "@saleor/macaw-ui";
|
||||
import { Box, Text } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import AppsSkeleton from "../../../apps/components/AppsSkeleton";
|
||||
import { useStyles } from "../../../apps/styles";
|
||||
import DeactivatedText from "../../../new-apps/components/DeactivatedText";
|
||||
import CustomAppsSkeleton from "../CustomAppsSkeleton";
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
export interface CustomAppListPageProps {
|
||||
appsList: AppListItemFragment[];
|
||||
|
@ -29,7 +29,7 @@ const CustomAppListPage: React.FC<CustomAppListPageProps> = ({
|
|||
getCustomAppHref,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles({});
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<ListPageLayout>
|
||||
|
@ -91,7 +91,7 @@ const CustomAppListPage: React.FC<CustomAppListPageProps> = ({
|
|||
</TableCell>
|
||||
</TableRowLink>
|
||||
) : (
|
||||
<AppsSkeleton key={index} />
|
||||
<CustomAppsSkeleton key={index} />
|
||||
),
|
||||
() => (
|
||||
<TableRowLink className={classes.tableRow}>
|
||||
|
|
44
src/custom-apps/components/CustomAppListPage/styles.ts
Normal file
44
src/custom-apps/components/CustomAppListPage/styles.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
[theme.breakpoints.up("lg")]: {
|
||||
colName: {
|
||||
paddingLeft: "0 !important",
|
||||
"&&": {
|
||||
width: "auto",
|
||||
},
|
||||
},
|
||||
},
|
||||
appName: {
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
colAction: {
|
||||
"&&": {
|
||||
paddingRight: theme.spacing(3),
|
||||
textAlign: "right",
|
||||
},
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
textAlign: "right",
|
||||
gap: theme.spacing(1),
|
||||
},
|
||||
colName: {
|
||||
paddingLeft: 0,
|
||||
minWidth: theme.spacing(30),
|
||||
},
|
||||
statusWrapper: {
|
||||
display: "inline-block",
|
||||
marginLeft: theme.spacing(2.5),
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer",
|
||||
},
|
||||
text: {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}),
|
||||
{ name: "CustomAppListPage" },
|
||||
);
|
|
@ -3,10 +3,10 @@ import TableRowLink from "@dashboard/components/TableRowLink";
|
|||
import { TableCell } from "@material-ui/core";
|
||||
import React from "react";
|
||||
|
||||
import { useStyles } from "../../styles";
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
export const AppsSkeleton = () => {
|
||||
const classes = useStyles({});
|
||||
export const CustomAppsSkeleton = () => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<TableRowLink className={classes.tableRow}>
|
||||
|
@ -17,5 +17,5 @@ export const AppsSkeleton = () => {
|
|||
);
|
||||
};
|
||||
|
||||
AppsSkeleton.displayName = "AppsSkeleton";
|
||||
export default AppsSkeleton;
|
||||
CustomAppsSkeleton.displayName = "CustomAppsSkeleton";
|
||||
export default CustomAppsSkeleton;
|
2
src/custom-apps/components/CustomAppsSkeleton/index.ts
Normal file
2
src/custom-apps/components/CustomAppsSkeleton/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from "./CustomAppsSkeleton";
|
||||
export { default } from "./CustomAppsSkeleton";
|
22
src/custom-apps/components/CustomAppsSkeleton/styles.ts
Normal file
22
src/custom-apps/components/CustomAppsSkeleton/styles.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
[theme.breakpoints.up("lg")]: {
|
||||
colName: {
|
||||
paddingLeft: "0 !important",
|
||||
"&&": {
|
||||
width: "auto",
|
||||
},
|
||||
},
|
||||
},
|
||||
colName: {
|
||||
paddingLeft: 0,
|
||||
minWidth: theme.spacing(30),
|
||||
},
|
||||
tableRow: {
|
||||
cursor: "pointer",
|
||||
},
|
||||
}),
|
||||
{ name: "CustomAppsSkeleton" },
|
||||
);
|
|
@ -1,6 +1,6 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import DialogButtons from "@dashboard/components/ActionDialog/DialogButtons";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import GiftCardTagInput from "@dashboard/giftCards/components/GiftCardTagInput";
|
||||
import {
|
||||
GiftCardSettingsExpiryTypeEnum,
|
||||
|
@ -50,21 +50,17 @@ interface GiftCardBulkCreateDialogFormProps {
|
|||
onClose: () => void;
|
||||
}
|
||||
|
||||
const GiftCardBulkCreateDialogForm: React.FC<GiftCardBulkCreateDialogFormProps> = ({
|
||||
onSubmit,
|
||||
opts,
|
||||
onClose,
|
||||
formErrors = {},
|
||||
}) => {
|
||||
const GiftCardBulkCreateDialogForm: React.FC<
|
||||
GiftCardBulkCreateDialogFormProps
|
||||
> = ({ onSubmit, opts, onClose, formErrors = {} }) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles({});
|
||||
|
||||
const {
|
||||
data: settingsData,
|
||||
loading: loadingSettings,
|
||||
} = useGiftCardSettingsQuery();
|
||||
const { data: settingsData, loading: loadingSettings } =
|
||||
useGiftCardSettingsQuery();
|
||||
|
||||
const getInitialExpirySettingsData = (): Partial<GiftCardBulkCreateFormData> => {
|
||||
const getInitialExpirySettingsData =
|
||||
(): Partial<GiftCardBulkCreateFormData> => {
|
||||
if (loadingSettings) {
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import useClipboard from "@dashboard/hooks/useClipboard";
|
||||
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||
import { buttonMessages } from "@dashboard/intl";
|
||||
|
@ -16,10 +16,9 @@ interface GiftCardCreateDialogCodeContentProps {
|
|||
onClose: () => void;
|
||||
}
|
||||
|
||||
const GiftCardCreateDialogCodeContent: React.FC<GiftCardCreateDialogCodeContentProps> = ({
|
||||
cardCode,
|
||||
onClose,
|
||||
}) => {
|
||||
const GiftCardCreateDialogCodeContent: React.FC<
|
||||
GiftCardCreateDialogCodeContentProps
|
||||
> = ({ cardCode, onClose }) => {
|
||||
const classes = useStyles({});
|
||||
const intl = useIntl();
|
||||
const notify = useNotifier();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import DialogButtons from "@dashboard/components/ActionDialog/DialogButtons";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import GiftCardTagInput from "@dashboard/giftCards/components/GiftCardTagInput";
|
||||
import {
|
||||
GiftCardErrorFragment,
|
||||
|
@ -69,14 +69,13 @@ const GiftCardCreateDialogForm: React.FC<GiftCardCreateDialogFormProps> = ({
|
|||
const intl = useIntl();
|
||||
const classes = useStyles({});
|
||||
|
||||
const {
|
||||
data: settingsData,
|
||||
loading: loadingSettings,
|
||||
} = useGiftCardSettingsQuery();
|
||||
const { data: settingsData, loading: loadingSettings } =
|
||||
useGiftCardSettingsQuery();
|
||||
|
||||
const [selectedCustomer, setSelectedCustomer] = useState<
|
||||
GiftCardCreateFormCustomer
|
||||
>(initialCustomer || defaultInitialCustomer);
|
||||
const [selectedCustomer, setSelectedCustomer] =
|
||||
useState<GiftCardCreateFormCustomer>(
|
||||
initialCustomer || defaultInitialCustomer,
|
||||
);
|
||||
|
||||
const handleSubmit = (data: GiftCardCreateFormData) =>
|
||||
onSubmit({ ...data, selectedCustomer });
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import ControlledCheckbox from "@dashboard/components/ControlledCheckbox";
|
||||
import RadioGroupField from "@dashboard/components/RadioGroupField";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import TimePeriodField from "@dashboard/giftCards/components/TimePeriodField";
|
||||
import {
|
||||
GiftCardBulkCreateFormErrors,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { appPath } from "@dashboard/apps/urls";
|
||||
import Link from "@dashboard/components/Link";
|
||||
import { TimelineEvent } from "@dashboard/components/Timeline";
|
||||
import { customerPath } from "@dashboard/customers/urls";
|
||||
import { GiftCardEventFragment, GiftCardEventsEnum } from "@dashboard/graphql";
|
||||
import { AppPaths } from "@dashboard/new-apps/urls";
|
||||
import { orderUrl } from "@dashboard/orders/urls";
|
||||
import { staffMemberDetailsUrl } from "@dashboard/staff/urls";
|
||||
import React from "react";
|
||||
|
@ -33,7 +33,7 @@ const getUserOrAppUrl = (event: GiftCardEventFragment): string => {
|
|||
return staffMemberDetailsUrl(event.user.id);
|
||||
}
|
||||
if (event.app) {
|
||||
return appPath(event.app.id);
|
||||
return AppPaths.resolveAppPath(event.app.id);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
@ -99,7 +99,7 @@ const getEventMessage = (event: GiftCardEventFragment, intl: IntlShape) => {
|
|||
href={
|
||||
event.user
|
||||
? customerPath(event.user.id)
|
||||
: appPath(event.app.id)
|
||||
: AppPaths.resolveAppPath(event.app.id)
|
||||
}
|
||||
>{`${content} ${user}`}</Link>
|
||||
),
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import ActionDialog from "@dashboard/components/ActionDialog";
|
||||
import { useChannelsSearch } from "@dashboard/components/ChannelsAvailabilityDialog/utils";
|
||||
import ControlledCheckbox from "@dashboard/components/ControlledCheckbox";
|
||||
import { IMessage } from "@dashboard/components/messages";
|
||||
import SingleAutocompleteSelectField from "@dashboard/components/SingleAutocompleteSelectField";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import {
|
||||
useChannelsQuery,
|
||||
useGiftCardResendMutation,
|
||||
|
@ -81,10 +81,8 @@ const GiftCardResendCodeDialog: React.FC<DialogProps> = ({ open, onClose }) => {
|
|||
handleSubmit,
|
||||
);
|
||||
|
||||
const [
|
||||
resendGiftCardCode,
|
||||
resendGiftCardCodeOpts,
|
||||
] = useGiftCardResendMutation({
|
||||
const [resendGiftCardCode, resendGiftCardCodeOpts] =
|
||||
useGiftCardResendMutation({
|
||||
onCompleted: data => {
|
||||
const errors = data?.giftCardResend?.errors;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import Money from "@dashboard/components/Money";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import clsx from "clsx";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import GiftCardTagInput from "@dashboard/giftCards/components/GiftCardTagInput";
|
||||
import GiftCardUpdateExpirySelect from "@dashboard/giftCards/GiftCardUpdate/GiftCardUpdateExpirySelect";
|
||||
import { Card, CardContent, Divider, Typography } from "@material-ui/core";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import ControlledCheckbox from "@dashboard/components/ControlledCheckbox";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { getGiftCardErrorMessage } from "@dashboard/giftCards/GiftCardUpdate/messages";
|
||||
import useGiftCardUpdateForm from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardUpdateFormProvider/hooks/useGiftCardUpdateForm";
|
||||
import useStateFromProps from "@dashboard/hooks/useStateFromProps";
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { appUrl } from "@dashboard/apps/urls";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import Link from "@dashboard/components/Link";
|
||||
import { customerUrl } from "@dashboard/customers/urls";
|
||||
import { GiftCardEventsEnum } from "@dashboard/graphql";
|
||||
import useDateLocalize from "@dashboard/hooks/useDateLocalize";
|
||||
import { getFullName, getStringOrPlaceholder } from "@dashboard/misc";
|
||||
import { AppUrls } from "@dashboard/new-apps/urls";
|
||||
import Label from "@dashboard/orders/components/OrderHistory/Label";
|
||||
import { getOrderNumberLinkObject } from "@dashboard/orders/components/OrderHistory/utils";
|
||||
import { getByType } from "@dashboard/orders/components/OrderReturnPage/utils";
|
||||
|
@ -51,7 +51,7 @@ const GiftCardUpdateInfoCardContent: React.FC = () => {
|
|||
return {
|
||||
label: messages.issuedByAppLabel,
|
||||
name: app?.name,
|
||||
url: appUrl(app?.id),
|
||||
url: AppUrls.resolveAppUrl(app?.id),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import GiftCardStatusChip from "@dashboard/giftCards/components/GiftCardStatusChip/GiftCardStatusChip";
|
||||
import { giftCardsListPath } from "@dashboard/giftCards/urls";
|
||||
import { getStringOrPlaceholder } from "@dashboard/misc";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import React from "react";
|
||||
|
||||
import GiftCardsListHeader from "./GiftCardsListHeader";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import CardMenu, { CardMenuItem } from "@dashboard/components/CardMenu";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import React from "react";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import Checkbox from "@dashboard/components/Checkbox";
|
||||
import DeleteIconButton from "@dashboard/components/DeleteIconButton";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import Link from "@dashboard/components/Link";
|
||||
import Money from "@dashboard/components/Money";
|
||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
|
@ -36,13 +36,8 @@ const GiftCardsListTable: React.FC = () => {
|
|||
const classes = useStyles({});
|
||||
const navigate = useNavigator();
|
||||
|
||||
const {
|
||||
toggle,
|
||||
isSelected,
|
||||
giftCards,
|
||||
numberOfColumns,
|
||||
params,
|
||||
} = useGiftCardList();
|
||||
const { toggle, isSelected, giftCards, numberOfColumns, params } =
|
||||
useGiftCardList();
|
||||
const { openDeleteDialog } = useGiftCardListDialogs();
|
||||
|
||||
const isCurrencySelected = !!params.currency;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import CollectionWithDividers from "@dashboard/components/CollectionWithDividers";
|
||||
import Link from "@dashboard/components/Link";
|
||||
import PreviewPill from "@dashboard/components/PreviewPill";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { useCustomerDetails } from "@dashboard/customers/hooks/useCustomerDetails";
|
||||
import GiftCardCreateDialogContent from "@dashboard/giftCards/GiftCardCreateDialog/GiftCardCreateDialogContent";
|
||||
import { getExtendedGiftCard } from "@dashboard/giftCards/GiftCardUpdate/providers/GiftCardDetailsProvider/utils";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import useAppChannel from "@dashboard/components/AppLayout/AppChannelContext";
|
||||
import ControlledCheckbox from "@dashboard/components/ControlledCheckbox";
|
||||
import SingleSelectField from "@dashboard/components/SingleSelectField";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import GiftCardCustomerSelectField from "@dashboard/giftCards/GiftCardCreateDialog/GiftCardCustomerSelectField";
|
||||
import { GiftCardCreateFormCustomer } from "@dashboard/giftCards/GiftCardCreateDialog/types";
|
||||
import { FormChange } from "@dashboard/hooks/useForm";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import ControlledCheckbox from "@dashboard/components/ControlledCheckbox";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { getGiftCardSettingsErrorMessage } from "@dashboard/giftCards/GiftCardSettings/messages";
|
||||
import {
|
||||
GiftCardSettingsErrorFragment,
|
||||
|
@ -22,7 +22,9 @@ export interface GiftCardSettingsExpirySelectProps {
|
|||
errors?: Record<"expiryPeriod", GiftCardSettingsErrorFragment>;
|
||||
}
|
||||
|
||||
const GiftCardSettingsExpirySelect: React.FC<GiftCardSettingsExpirySelectProps> = ({
|
||||
const GiftCardSettingsExpirySelect: React.FC<
|
||||
GiftCardSettingsExpirySelectProps
|
||||
> = ({
|
||||
errors,
|
||||
change,
|
||||
disabled,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { appsSection } from "@dashboard/apps/urls";
|
||||
import { AppSections } from "@dashboard/new-apps/urls";
|
||||
import { matchPath, useLocation } from "react-router";
|
||||
|
||||
const isAppPath = (pathname: string) =>
|
||||
!!matchPath(pathname, {
|
||||
path: `${appsSection}:id`,
|
||||
path: `${AppSections.appsSection}:id`,
|
||||
});
|
||||
|
||||
/*
|
|
@ -15,7 +15,6 @@ import TagManager from "react-gtm-module";
|
|||
import { useIntl } from "react-intl";
|
||||
import { BrowserRouter, Route, Switch } from "react-router-dom";
|
||||
|
||||
import { useLocationState } from "./apps/hooks/useLocationState";
|
||||
import AttributeSection from "./attributes";
|
||||
import { attributeSection } from "./attributes/urls";
|
||||
import Auth, { useUser } from "./auth";
|
||||
|
@ -52,6 +51,7 @@ import { giftCardsSectionUrlName } from "./giftCards/urls";
|
|||
import { apolloClient, saleorClient } from "./graphql/client";
|
||||
import HomePage from "./home";
|
||||
import { FlagsServiceProvider } from "./hooks/useFlags/flagsService";
|
||||
import { useLocationState } from "./hooks/useLocationState";
|
||||
import { commonMessages } from "./intl";
|
||||
import MarketplaceSection from "./marketplace";
|
||||
import { marketplaceUrl } from "./marketplace/urls";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { appsListUrl } from "@dashboard/apps/urls";
|
||||
import { TopNavLink, TopNavWrapper } from "@dashboard/components/AppLayout";
|
||||
import { LinkState } from "@dashboard/components/Link";
|
||||
import { AppAvatar } from "@dashboard/new-apps/components/AppAvatar/AppAvatar";
|
||||
import { AppUrls } from "@dashboard/new-apps/urls";
|
||||
import { Box, Button, Text } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
@ -19,7 +19,7 @@ export const AppPageNav: React.FC<AppPageNavProps> = ({
|
|||
homepageUrl,
|
||||
}) => {
|
||||
const location = useLocation<LinkState>();
|
||||
const goBackLink = location.state?.from ?? appsListUrl();
|
||||
const goBackLink = location.state?.from ?? AppUrls.resolveAppListUrl();
|
||||
|
||||
return (
|
||||
<TopNavWrapper>
|
||||
|
|
|
@ -105,6 +105,21 @@ export const AppUrls = {
|
|||
const appCompleteUrl = urlJoin(appUrl, deepSubPath);
|
||||
return appCompleteUrl;
|
||||
},
|
||||
resolveDashboardUrlFromAppCompleteUrl: (
|
||||
appCompleteUrl: string,
|
||||
appUrl?: string,
|
||||
appId?: string,
|
||||
) => {
|
||||
if (!appUrl || !appId) {
|
||||
return appUrl;
|
||||
}
|
||||
const deepSubPath = appCompleteUrl.replace(appUrl, "");
|
||||
const dashboardUrl = urlJoin(
|
||||
AppPaths.resolveAppPath(encodeURIComponent(appId)),
|
||||
deepSubPath,
|
||||
);
|
||||
return dashboardUrl;
|
||||
},
|
||||
resolveAppIframeUrl: (
|
||||
appId: string,
|
||||
appUrl: string,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import DefaultCardTitle from "@dashboard/components/CardTitle";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import { FulfillmentStatus } from "@dashboard/graphql";
|
||||
import { StatusType } from "@dashboard/types";
|
||||
import { Typography } from "@material-ui/core";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import Checkbox from "@dashboard/components/Checkbox";
|
||||
import ConfirmButton from "@dashboard/components/ConfirmButton";
|
||||
import FormSpacer from "@dashboard/components/FormSpacer";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { AddressTypeInput } from "@dashboard/customers/types";
|
||||
import {
|
||||
AddressFragment,
|
||||
|
@ -71,7 +71,9 @@ const defaultSearchState: OrderCustomerSearchAddressState = {
|
|||
type: undefined,
|
||||
};
|
||||
|
||||
const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialogProps> = props => {
|
||||
const OrderCustomerAddressesEditDialog: React.FC<
|
||||
OrderCustomerAddressesEditDialogProps
|
||||
> = props => {
|
||||
const {
|
||||
open,
|
||||
variant,
|
||||
|
@ -94,14 +96,10 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
|
|||
const hasCustomerChanged =
|
||||
variant === AddressEditDialogVariant.CHANGE_CUSTOMER;
|
||||
|
||||
const {
|
||||
errors: shippingValidationErrors,
|
||||
submit: handleShippingSubmit,
|
||||
} = useAddressValidation(address => address, AddressTypeEnum.SHIPPING);
|
||||
const {
|
||||
errors: billingValidationErrors,
|
||||
submit: handleBillingSubmit,
|
||||
} = useAddressValidation(address => address, AddressTypeEnum.BILLING);
|
||||
const { errors: shippingValidationErrors, submit: handleShippingSubmit } =
|
||||
useAddressValidation(address => address, AddressTypeEnum.SHIPPING);
|
||||
const { errors: billingValidationErrors, submit: handleBillingSubmit } =
|
||||
useAddressValidation(address => address, AddressTypeEnum.BILLING);
|
||||
|
||||
const dialogErrors = useModalDialogErrors(
|
||||
[...errors, ...shippingValidationErrors, ...billingValidationErrors],
|
||||
|
@ -223,9 +221,8 @@ const OrderCustomerAddressesEditDialog: React.FC<OrderCustomerAddressesEditDialo
|
|||
|
||||
const countryChoices = mapCountriesToChoices(countries);
|
||||
|
||||
const [addressSearchState, setAddressSearchState] = React.useState<
|
||||
OrderCustomerSearchAddressState
|
||||
>(defaultSearchState);
|
||||
const [addressSearchState, setAddressSearchState] =
|
||||
React.useState<OrderCustomerSearchAddressState>(defaultSearchState);
|
||||
|
||||
const validatedDefaultShippingAddress = validateDefaultAddress(
|
||||
defaultShippingAddress,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import { ConfirmButton } from "@dashboard/components/ConfirmButton";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import CustomerAddressChoiceCard from "@dashboard/customers/components/CustomerAddressChoiceCard";
|
||||
import { AddressFragment, AddressTypeEnum } from "@dashboard/graphql";
|
||||
import { FormChange } from "@dashboard/hooks/useForm";
|
||||
|
@ -38,7 +38,9 @@ export interface OrderCustomerAddressesSearchProps {
|
|||
exitSearch();
|
||||
}
|
||||
|
||||
const OrderCustomerAddressesSearch: React.FC<OrderCustomerAddressesSearchProps> = props => {
|
||||
const OrderCustomerAddressesSearch: React.FC<
|
||||
OrderCustomerAddressesSearchProps
|
||||
> = props => {
|
||||
const {
|
||||
type,
|
||||
cloneAddress,
|
||||
|
@ -60,10 +62,8 @@ const OrderCustomerAddressesSearch: React.FC<OrderCustomerAddressesSearchProps>
|
|||
);
|
||||
|
||||
const [query, setQuery] = React.useState("");
|
||||
const [
|
||||
temporarySelectedAddress,
|
||||
setTemporarySelectedAddress,
|
||||
] = React.useState(initialAddress);
|
||||
const [temporarySelectedAddress, setTemporarySelectedAddress] =
|
||||
React.useState(initialAddress);
|
||||
|
||||
const handleSelect = () => {
|
||||
if (type === AddressTypeEnum.SHIPPING) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import Link from "@dashboard/components/Link";
|
||||
import Money from "@dashboard/components/Money";
|
||||
import {
|
||||
|
@ -66,7 +66,9 @@ interface OrderDraftDetailsSummaryProps
|
|||
onShippingMethodEdit: () => void;
|
||||
}
|
||||
|
||||
const OrderDraftDetailsSummary: React.FC<OrderDraftDetailsSummaryProps> = props => {
|
||||
const OrderDraftDetailsSummary: React.FC<
|
||||
OrderDraftDetailsSummaryProps
|
||||
> = props => {
|
||||
const {
|
||||
order,
|
||||
errors,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import { TimelineEvent } from "@dashboard/components/Timeline";
|
||||
import { TitleElement } from "@dashboard/components/Timeline/TimelineEventHeader";
|
||||
import { OrderEventFragment, OrderEventsEnum } from "@dashboard/graphql";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import { DiscountValueTypeEnum, MoneyFragment } from "@dashboard/graphql";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import { Hr } from "@dashboard/components/Hr";
|
||||
import Money from "@dashboard/components/Money";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import { ChannelShippingData } from "@dashboard/channels/utils";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import ControlledCheckbox from "@dashboard/components/ControlledCheckbox";
|
||||
|
@ -6,6 +5,7 @@ import PriceField from "@dashboard/components/PriceField";
|
|||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
import TableHead from "@dashboard/components/TableHead";
|
||||
import TableRowLink from "@dashboard/components/TableRowLink";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { ShippingChannelsErrorFragment } from "@dashboard/graphql";
|
||||
import { ChangeEvent } from "@dashboard/hooks/useForm";
|
||||
import {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import ControlledCheckbox from "@dashboard/components/ControlledCheckbox";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { ShippingErrorFragment } from "@dashboard/graphql";
|
||||
import { ChangeEvent } from "@dashboard/hooks/useForm";
|
||||
import useShop from "@dashboard/hooks/useShop";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { CountryFragment } from "@dashboard/graphql";
|
||||
import { useLocalSearch } from "@dashboard/hooks/useLocalSearch";
|
||||
import useModalDialogOpen from "@dashboard/hooks/useModalDialogOpen";
|
||||
|
@ -36,9 +36,8 @@ export const TaxCountryDialog: React.FC<TaxCountryDialogProps> = ({
|
|||
const classes = useStyles();
|
||||
const intl = useIntl();
|
||||
|
||||
const [selectedCountry, setSelectedCountry] = React.useState<
|
||||
CountryFragment
|
||||
>();
|
||||
const [selectedCountry, setSelectedCountry] =
|
||||
React.useState<CountryFragment>();
|
||||
|
||||
useModalDialogOpen(open, {
|
||||
onClose: () => {
|
||||
|
@ -47,9 +46,11 @@ export const TaxCountryDialog: React.FC<TaxCountryDialogProps> = ({
|
|||
},
|
||||
});
|
||||
|
||||
const { query, setQuery, searchResult: filteredCountries } = useLocalSearch<
|
||||
CountryFragment
|
||||
>(countries, country => country.country);
|
||||
const {
|
||||
query,
|
||||
setQuery,
|
||||
searchResult: filteredCountries,
|
||||
} = useLocalSearch<CountryFragment>(countries, country => country.country);
|
||||
|
||||
return (
|
||||
<Dialog open={open} fullWidth onClose={onClose} className={classes.dialog}>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import HorizontalSpacer from "@dashboard/apps/components/HorizontalSpacer";
|
||||
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
|
||||
import PreviewPill from "@dashboard/components/PreviewPill";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import Form from "@dashboard/components/Form";
|
||||
|
@ -6,6 +5,7 @@ import Grid from "@dashboard/components/Grid";
|
|||
import { DetailPageLayout } from "@dashboard/components/Layouts";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { configurationMenuUrl } from "@dashboard/configuration";
|
||||
import {
|
||||
CountryCode,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import Grid from "@dashboard/components/Grid";
|
||||
|
@ -6,6 +5,7 @@ import { DetailPageLayout } from "@dashboard/components/Layouts";
|
|||
import Metadata from "@dashboard/components/Metadata";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { configurationMenuUrl } from "@dashboard/configuration";
|
||||
import { TaxClassFragment } from "@dashboard/graphql";
|
||||
import { useClientPagination } from "@dashboard/hooks/useClientPagination/useClientPagination";
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import Grid from "@dashboard/components/Grid";
|
||||
import { DetailPageLayout } from "@dashboard/components/Layouts";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import VerticalSpacer from "@dashboard/components/VerticalSpacer";
|
||||
import { configurationMenuUrl } from "@dashboard/configuration";
|
||||
import {
|
||||
CountryCode,
|
||||
|
|
Loading…
Reference in a new issue