New macaw ui (#3069)
Co-authored-by: Krzysztof Żuraw <9116238+krzysztofzuraw@users.noreply.github.com> Co-authored-by: Michał Droń <dron.official@yahoo.com> Co-authored-by: Paweł Chyła <chyla1988@gmail.com>
This commit is contained in:
parent
91bd9c772d
commit
3789f5bb52
279 changed files with 11146 additions and 21463 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -21,7 +21,6 @@
|
|||
!.travis*
|
||||
!.tx
|
||||
!.husky
|
||||
*.css
|
||||
*.log
|
||||
*.pyc
|
||||
*.mo
|
||||
|
|
BIN
assets/images/sidebar-default-logo.png
Normal file
BIN
assets/images/sidebar-default-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4 KiB |
|
@ -116,6 +116,7 @@ describe("As an admin I want to manage categories", () => {
|
|||
createCategory({ name: categoryName, description: categoryName })
|
||||
.visit(categoryDetailsUrl(category.id))
|
||||
.contains(CATEGORY_DETAILS.categoryChildrenRow, categoryName)
|
||||
.scrollIntoView()
|
||||
.should("be.visible");
|
||||
getCategory(category.id).then(categoryResp => {
|
||||
expect(categoryResp.children.edges[0].node.name).to.eq(categoryName);
|
||||
|
|
|
@ -9,7 +9,8 @@ import {
|
|||
import { PERMISSIONS_OPTIONS } from "../fixtures/permissionsUsers";
|
||||
import * as permissionsSteps from "../support/pages/permissionsPage";
|
||||
|
||||
describe("As a staff user I want to navigate through shop using different permissions", () => {
|
||||
// TODO: fix this test after release of new dashboard with sidebar
|
||||
describe.skip("As a staff user I want to navigate through shop using different permissions", () => {
|
||||
const permissionsOptions = PERMISSIONS_OPTIONS;
|
||||
|
||||
before(() => {
|
||||
|
|
|
@ -11,21 +11,21 @@ export const LEFT_MENU_SELECTORS = {
|
|||
customers: "[data-test-id='menu-item-label-customers']",
|
||||
};
|
||||
export const DISCOUNTS_MENU_SELECTORS = {
|
||||
sales: "[data-test-id='submenu-item-label-sales']",
|
||||
vouchers: "[data-test-id='submenu-item-label-vouchers']",
|
||||
sales: "[data-test-id='menu-item-label-sales']",
|
||||
vouchers: "[data-test-id='menu-item-label-vouchers']",
|
||||
};
|
||||
export const ORDERS = {
|
||||
orders: "[data-test-id='submenu-item-label-orders']",
|
||||
draftOrders: "[data-test-id='submenu-item-label-order-drafts']",
|
||||
orders: "[data-test-id='menu-item-label-orders']",
|
||||
draftOrders: "[data-test-id='menu-item-label-order-drafts']",
|
||||
};
|
||||
export const CATALOG = {
|
||||
products: "[data-test-id='submenu-item-label-products']",
|
||||
categories: "[data-test-id='submenu-item-label-categories']",
|
||||
collections: "[data-test-id='submenu-item-label-collections']",
|
||||
products: "[data-test-id='menu-item-label-products']",
|
||||
categories: "[data-test-id='menu-item-label-categories']",
|
||||
collections: "[data-test-id='menu-item-label-collections']",
|
||||
};
|
||||
|
||||
export const APP_MENU_SELECTORS = {
|
||||
app: "[data-test-id='submenu-item-label-apps']",
|
||||
app: "[data-test-id='menu-item-label-apps']",
|
||||
};
|
||||
|
||||
export const appCommonSelector = "[data-test-id*='apps']";
|
||||
|
|
|
@ -2,8 +2,8 @@ export const CATEGORY_DETAILS = {
|
|||
nameInput: '[name="name"]',
|
||||
descriptionInput: '[data-test-id="rich-text-editor-description"]',
|
||||
createSubcategoryButton: '[data-test-id="create-subcategory"]',
|
||||
categoryChildrenRow: '[data-test-id*="id"]',
|
||||
categoryChildrenRow: "[data-test-id^='id-']",
|
||||
productsTab: '[data-test-id="products-tab"]',
|
||||
addProducts: '[data-test-id="add-products"]',
|
||||
productRow: '[data-test-id="product-row"]'
|
||||
productRow: '[data-test-id="product-row"]',
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export const PRODUCTS_LIST = {
|
||||
productsList: "[data-test-id*='id']",
|
||||
productsList: "[data-test-id^='id-']",
|
||||
productsNames: "[data-test-id='name']",
|
||||
dialogProductTypeInput: "[data-test-id='dialog-product-type']",
|
||||
createProductBtn: "[data-test-id='add-product']",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export const ORDERS_SELECTORS = {
|
||||
orders: "[data-test-id='submenu-item-label'][data-test-id='orders']",
|
||||
orders: "[data-test-id='menu-item-label'][data-test-id='orders']",
|
||||
createOrder: "[data-test-id='create-order-button']",
|
||||
orderRow: "[data-test-id='order-table-row']",
|
||||
salesChannel: "[data-test-id='order-sales-channel']",
|
||||
|
|
|
@ -40,6 +40,8 @@ export function fillUpShippingZoneData({
|
|||
.get(SHIPPING_ZONE_DETAILS.autocompleteContentDialog);
|
||||
cy.contains(SHIPPING_ZONE_DETAILS.option, warehouseName)
|
||||
.click({ force: true })
|
||||
.get(SHIPPING_ZONE_DETAILS.warehouseSelector)
|
||||
.click()
|
||||
.get(SHIPPING_ZONE_DETAILS.channelSelector)
|
||||
.click()
|
||||
.get(SHIPPING_ZONE_DETAILS.option)
|
||||
|
|
|
@ -543,10 +543,6 @@
|
|||
"context": "order status",
|
||||
"string": "Partially returned"
|
||||
},
|
||||
"26VlBZ": {
|
||||
"context": "information",
|
||||
"string": "Problem occured during installation."
|
||||
},
|
||||
"28GZnc": {
|
||||
"string": "Start typing to begin search..."
|
||||
},
|
||||
|
@ -966,6 +962,9 @@
|
|||
"5LRkEs": {
|
||||
"string": "The new dashboard and the GraphQL API are preview-quality software."
|
||||
},
|
||||
"5ObBlW": {
|
||||
"string": "Dark Mode"
|
||||
},
|
||||
"5OtU+V": {
|
||||
"context": "dialog title",
|
||||
"string": "Unassign products from collection"
|
||||
|
@ -1716,6 +1715,10 @@
|
|||
"context": "header",
|
||||
"string": "Activity"
|
||||
},
|
||||
"BYGJ/j": {
|
||||
"context": "button label",
|
||||
"string": "Settings"
|
||||
},
|
||||
"BZ7BkQ": {
|
||||
"context": "capture payment, button",
|
||||
"string": "Capture"
|
||||
|
@ -2391,9 +2394,6 @@
|
|||
"context": "webhooks inactive label",
|
||||
"string": "Inactive"
|
||||
},
|
||||
"GOdq5V": {
|
||||
"string": "Catalog"
|
||||
},
|
||||
"GVM/fi": {
|
||||
"context": "order history message",
|
||||
"string": "Payment was authorized"
|
||||
|
@ -3333,6 +3333,9 @@
|
|||
"context": "webhook input label",
|
||||
"string": "Secret Key"
|
||||
},
|
||||
"NQgbYA": {
|
||||
"string": "Account Settings"
|
||||
},
|
||||
"NUevU9": {
|
||||
"context": "attribute values list: slug column header",
|
||||
"string": "Swatch"
|
||||
|
@ -3572,9 +3575,6 @@
|
|||
"context": "dialog header",
|
||||
"string": "Cancel Order"
|
||||
},
|
||||
"PRlD0A": {
|
||||
"string": "Shipping"
|
||||
},
|
||||
"PTW56s": {
|
||||
"context": "alert",
|
||||
"string": "Channel limit reached"
|
||||
|
@ -3886,6 +3886,10 @@
|
|||
"context": "copy code button label",
|
||||
"string": "Copy code"
|
||||
},
|
||||
"RY4PJY": {
|
||||
"context": "information",
|
||||
"string": "Installation failed"
|
||||
},
|
||||
"RZ32u5": {
|
||||
"context": "PageTypeDeleteWarningDialog single consent label",
|
||||
"string": "Yes, I want to delete this page type and assigned pages"
|
||||
|
@ -5895,6 +5899,9 @@
|
|||
"context": "channel publication date",
|
||||
"string": "Will become published on {date}"
|
||||
},
|
||||
"hVPucN": {
|
||||
"string": "Light Mode"
|
||||
},
|
||||
"hWO1SD": {
|
||||
"context": "order history message",
|
||||
"string": "Draft order was created"
|
||||
|
@ -6387,6 +6394,10 @@
|
|||
"context": "selectt all options",
|
||||
"string": "Select All"
|
||||
},
|
||||
"lSd5Zo": {
|
||||
"context": "app permissions label",
|
||||
"string": "None"
|
||||
},
|
||||
"lT5MYM": {
|
||||
"context": "dialog title",
|
||||
"string": "Unassign users"
|
||||
|
@ -7008,6 +7019,9 @@
|
|||
"context": "bulk delete label",
|
||||
"string": "Delete"
|
||||
},
|
||||
"qnPzX7": {
|
||||
"string": "Channels that don’t have assigned discounts will use their parent channel to define the value. Value will be converted to channel’s currency"
|
||||
},
|
||||
"qov29K": {
|
||||
"context": "dialog content",
|
||||
"string": "Select one of customer addresses or add a new address:"
|
||||
|
@ -7948,9 +7962,6 @@
|
|||
"y/UWBR": {
|
||||
"string": "There is no address to show for this customer"
|
||||
},
|
||||
"y1Z3or": {
|
||||
"string": "Language"
|
||||
},
|
||||
"y7mfbl": {
|
||||
"string": "Currently, there are no countries assigned to this shipping zone"
|
||||
},
|
||||
|
|
18963
package-lock.json
generated
18963
package-lock.json
generated
File diff suppressed because it is too large
Load diff
11
package.json
11
package.json
|
@ -24,7 +24,6 @@
|
|||
"@editorjs/list": "^1.7.0",
|
||||
"@editorjs/paragraph": "^2.8.0",
|
||||
"@editorjs/quote": "^2.4.0",
|
||||
"@floating-ui/react-dom-interactions": "^0.5.0",
|
||||
"@glideapps/glide-data-grid": "^5.0.0",
|
||||
"@graphiql/plugin-explorer": "^0.1.12",
|
||||
"@graphiql/react": "^0.15.0",
|
||||
|
@ -34,13 +33,14 @@
|
|||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"@material-ui/styles": "^4.11.4",
|
||||
"@reach/auto-id": "^0.16.0",
|
||||
"@saleor/macaw-ui": "^0.7.2",
|
||||
"@saleor/macaw-ui": "^0.8.0-pre.31",
|
||||
"@saleor/sdk": "^0.4.4",
|
||||
"@sentry/react": "^6.0.0",
|
||||
"@types/faker": "^5.1.6",
|
||||
"@uiw/react-color-hue": "0.0.34",
|
||||
"@uiw/react-color-material": "^0.1.0",
|
||||
"@uiw/react-color-saturation": "0.0.34",
|
||||
"@vanilla-extract/css-utils": "^0.1.3",
|
||||
"apollo-upload-client": "^17.0.0",
|
||||
"clsx": "^1.2.1",
|
||||
"color-convert": "^2.0.1",
|
||||
|
@ -88,8 +88,10 @@
|
|||
"react-sortable-tree": "^2.6.2",
|
||||
"semver-compare": "^1.0.0",
|
||||
"slugify": "^1.4.6",
|
||||
"tslib": "^2.4.1",
|
||||
"url-join": "^4.0.1",
|
||||
"use-react-router": "^1.0.7"
|
||||
"use-react-router": "^1.0.7",
|
||||
"usehooks-ts": "^2.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.5.5",
|
||||
|
@ -126,7 +128,6 @@
|
|||
"@types/fuzzaldrin": "^2.1.2",
|
||||
"@types/is-ci": "^3.0.0",
|
||||
"@types/jscodeshift": "^0.11.3",
|
||||
"@types/lodash-es": "^4.17.3",
|
||||
"@types/pollyjs__adapter-node-http": "^2.0.1",
|
||||
"@types/pollyjs__persister-fs": "^2.0.1",
|
||||
"@types/react": "^17.0.50",
|
||||
|
@ -250,10 +251,10 @@
|
|||
"@locale(.*)$": "<rootDir>/locale/$1",
|
||||
"@dashboard(.*)$": "<rootDir>/src/$1",
|
||||
"@test/(.*)$": "<rootDir>/testUtils/$1",
|
||||
"^lodash-es(.*)$": "lodash/$1",
|
||||
"^@material-ui/core$": "<rootDir>/node_modules/@material-ui/core",
|
||||
"^@material-ui/icons$": "<rootDir>/node_modules/@material-ui/icons",
|
||||
"^@material-ui/styles$": "<rootDir>/node_modules/@material-ui/styles",
|
||||
"^@saleor/macaw-ui/next$": "<rootDir>/node_modules/@saleor/macaw-ui/dist/macaw-ui.cjs",
|
||||
"^react$": "<rootDir>/node_modules/react",
|
||||
"^react-dom$": "<rootDir>/node_modules/react-dom"
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import { appsListPath } from "@dashboard/apps/urls";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import ExternalLink from "@dashboard/components/ExternalLink";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import { AppQuery } from "@dashboard/graphql";
|
||||
import { buttonMessages, sectionNames } from "@dashboard/intl";
|
||||
import { buttonMessages } from "@dashboard/intl";
|
||||
import { ButtonBase, Card, CardContent, Typography } from "@material-ui/core";
|
||||
import { Box, Button } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import SVG from "react-inlinesvg";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
@ -42,11 +40,9 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
|
|||
const classes = useStyles({});
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Backlink href={appsListPath}>
|
||||
{intl.formatMessage(sectionNames.apps)}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
<>
|
||||
<TopNav
|
||||
href={appsListPath}
|
||||
title={
|
||||
<>
|
||||
{data?.name} {!data?.isActive && <DeactivatedText />}
|
||||
|
@ -60,8 +56,8 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
|
|||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<div className={classes.appHeader}>
|
||||
</TopNav>
|
||||
<Box marginX={10}>
|
||||
{data ? (
|
||||
<div className={classes.appHeaderLinks}>
|
||||
<ExternalLink
|
||||
|
@ -101,7 +97,7 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
|
|||
<Skeleton />
|
||||
)}
|
||||
<div className={classes.hr} />
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
<Card>
|
||||
<CardTitle
|
||||
|
@ -182,7 +178,7 @@ export const AppDetailsPage: React.FC<AppDetailsPageProps> = ({
|
|||
</Card>
|
||||
)}
|
||||
<CardSpacer />
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
import saleorDarkLogoSmall from "@assets/images/logo-dark-small.svg";
|
||||
import plusIcon from "@assets/images/plus-icon.svg";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import Hr from "@dashboard/components/Hr";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import { AppFetchMutation, AppInstallMutation } from "@dashboard/graphql";
|
||||
import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||
import { buttonMessages } from "@dashboard/intl";
|
||||
import { Card, CardContent, Grid, Typography } from "@material-ui/core";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CircularProgress,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import { Box, Button, GenericAppIcon } from "@saleor/macaw-ui/next";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
@ -37,109 +43,112 @@ export const AppInstallPage: React.FC<AppInstallPageProps> = ({
|
|||
const name = data?.name || "";
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<CardSpacer />
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={
|
||||
loading ? (
|
||||
<DetailedContent useSingleColumn constHeight>
|
||||
<Content>
|
||||
<CardSpacer />
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={
|
||||
loading ? (
|
||||
<Skeleton />
|
||||
) : (
|
||||
intl.formatMessage(
|
||||
{
|
||||
id: "Id7C0X",
|
||||
defaultMessage: `You are about to install {name}`,
|
||||
description: "section header",
|
||||
},
|
||||
{ name },
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<CardContent className={classes.installCard}>
|
||||
{loading ? (
|
||||
<CircularProgress />
|
||||
) : (
|
||||
<div className={classes.installAppContainer}>
|
||||
<div
|
||||
className={clsx(
|
||||
classes.installIcon,
|
||||
classes.installSaleorIcon,
|
||||
)}
|
||||
>
|
||||
<img src={saleorDarkLogoSmall} alt="" />
|
||||
</div>
|
||||
<img src={plusIcon} alt="" />
|
||||
<div className={classes.installIcon}>
|
||||
<GenericAppIcon />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<CardSpacer />
|
||||
<Card>
|
||||
{!loading && (
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
id: "VsGcdP",
|
||||
defaultMessage: "App permissions",
|
||||
description: "section header",
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
<CardContent>
|
||||
{loading ? (
|
||||
<Skeleton />
|
||||
) : (
|
||||
intl.formatMessage(
|
||||
{
|
||||
id: "Id7C0X",
|
||||
defaultMessage: `You are about to install {name}`,
|
||||
description: "section header",
|
||||
},
|
||||
{ name },
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
<CardContent className={classes.installCard}>
|
||||
{loading ? (
|
||||
<Skeleton />
|
||||
) : (
|
||||
<div className={classes.installAppContainer}>
|
||||
<div
|
||||
className={clsx(classes.installIcon, classes.installSaleorIcon)}
|
||||
>
|
||||
<img src={saleorDarkLogoSmall} alt="" />
|
||||
</div>
|
||||
<img src={plusIcon} alt="" />
|
||||
<div className={classes.installIcon}>
|
||||
<h2>{name?.charAt(0).toUpperCase()}</h2>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<CardSpacer />
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
id: "VsGcdP",
|
||||
defaultMessage: "App permissions",
|
||||
description: "section header",
|
||||
})}
|
||||
/>
|
||||
<CardContent>
|
||||
{loading ? (
|
||||
<Skeleton />
|
||||
) : (
|
||||
<>
|
||||
<Typography className={classes.installPermissionTitle}>
|
||||
<FormattedMessage
|
||||
id="BL/Lbk"
|
||||
defaultMessage="Installing this app will give it following permissions:"
|
||||
description="install app permissions"
|
||||
/>
|
||||
</Typography>
|
||||
{!!data?.permissions?.length && (
|
||||
<ul className={classes.permissionsContainer}>
|
||||
{data?.permissions?.map(perm => (
|
||||
<li key={perm.code}>{perm.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
<Hr className={classes.installSpacer} />
|
||||
|
||||
<Typography
|
||||
variant="body2"
|
||||
className={classes.installPrivacyText}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="t1UYU6"
|
||||
defaultMessage="Uninstalling the app will remove all your customer’s personal data stored by {name}. "
|
||||
description="install app privacy"
|
||||
values={{ name }}
|
||||
/>
|
||||
{!!data?.dataPrivacyUrl && (
|
||||
<a
|
||||
href={data?.dataPrivacyUrl}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="k5lHFp"
|
||||
defaultMessage="Learn more about data privacy"
|
||||
description="app data privacy link"
|
||||
/>
|
||||
</a>
|
||||
<>
|
||||
<Typography className={classes.installPermissionTitle}>
|
||||
<FormattedMessage
|
||||
id="BL/Lbk"
|
||||
defaultMessage="Installing this app will give it following permissions:"
|
||||
description="install app permissions"
|
||||
/>
|
||||
</Typography>
|
||||
{!!data?.permissions?.length && (
|
||||
<ul className={classes.permissionsContainer}>
|
||||
{data?.permissions?.map(perm => (
|
||||
<li key={perm.code}>{perm.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<CardSpacer />
|
||||
<Grid container justify="space-between">
|
||||
<Grid xs={6} item>
|
||||
<Hr className={classes.installSpacer} />
|
||||
|
||||
<Typography
|
||||
variant="body2"
|
||||
className={classes.installPrivacyText}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="t1UYU6"
|
||||
defaultMessage="Uninstalling the app will remove all your customer’s personal data stored by {name}. "
|
||||
description="install app privacy"
|
||||
values={{ name }}
|
||||
/>
|
||||
{!!data?.dataPrivacyUrl && (
|
||||
<a
|
||||
href={data?.dataPrivacyUrl}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="k5lHFp"
|
||||
defaultMessage="Learn more about data privacy"
|
||||
description="app data privacy link"
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<CardSpacer />
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Button variant="secondary" onClick={navigateToAppsList}>
|
||||
<FormattedMessage {...buttonMessages.cancel} />
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid xs={6} item className={classes.alignRight}>
|
||||
<Button variant="primary" onClick={onSubmit}>
|
||||
<FormattedMessage
|
||||
id="PkCmGU"
|
||||
|
@ -147,9 +156,9 @@ export const AppInstallPage: React.FC<AppInstallPageProps> = ({
|
|||
description="install button"
|
||||
/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
</Content>
|
||||
</DetailedContent>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Container from "@dashboard/components/Container";
|
||||
import { AppQuery } from "@dashboard/graphql";
|
||||
import React from "react";
|
||||
|
||||
|
@ -21,19 +20,17 @@ export const AppPage: React.FC<AppPageProps> = ({
|
|||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Container className={classes.container}>
|
||||
<div className={classes.iframeContainer}>
|
||||
{url && (
|
||||
<AppFrame
|
||||
src={url}
|
||||
appToken={data?.accessToken ?? ""}
|
||||
onError={onError}
|
||||
appId={data?.id ?? ""}
|
||||
refetch={refetch}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Container>
|
||||
<div className={classes.iframeContainer}>
|
||||
{url && (
|
||||
<AppFrame
|
||||
src={url}
|
||||
appToken={data?.accessToken ?? ""}
|
||||
onError={onError}
|
||||
appId={data?.id ?? ""}
|
||||
refetch={refetch}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ export const useStyles = makeStyles(
|
|||
height: "100%",
|
||||
"& > iframe": {
|
||||
border: "none",
|
||||
minHeight: "60vh",
|
||||
minHeight: "100vh",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
},
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
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 Container from "@dashboard/components/Container";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import {
|
||||
AppListItemFragment,
|
||||
AppsInstallationsQuery,
|
||||
|
@ -175,8 +174,8 @@ const AppsListPage: React.FC<AppsListPageProps> = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.apps)} />
|
||||
<>
|
||||
<TopNav title={intl.formatMessage(sectionNames.apps)} />
|
||||
<AppPageTabs
|
||||
showSaleorApps={saleorAppsEnabled}
|
||||
className={styles.topTabs}
|
||||
|
@ -184,7 +183,7 @@ const AppsListPage: React.FC<AppsListPageProps> = ({
|
|||
value={activeTab}
|
||||
/>
|
||||
{renderContent()}
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ export const useStyles = makeStyles(
|
|||
theme => ({
|
||||
[theme.breakpoints.up("lg")]: {
|
||||
colName: {
|
||||
paddingLeft: "0 !important",
|
||||
"&&": {
|
||||
width: "auto",
|
||||
},
|
||||
|
|
|
@ -64,12 +64,10 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
|
|||
installations.filter(item => item.id !== id),
|
||||
);
|
||||
|
||||
const {
|
||||
data: appsInProgressData,
|
||||
refetch: appsInProgressRefetch,
|
||||
} = useAppsInstallationsQuery({
|
||||
displayLoader: false,
|
||||
});
|
||||
const { data: appsInProgressData, refetch: appsInProgressRefetch } =
|
||||
useAppsInstallationsQuery({
|
||||
displayLoader: false,
|
||||
});
|
||||
const { data, loading, refetch } = useAppsListQuery({
|
||||
displayLoader: true,
|
||||
variables: {
|
||||
|
@ -121,18 +119,16 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
|
|||
AppListUrlQueryParams
|
||||
>(navigate, appsListUrl, params);
|
||||
|
||||
const [
|
||||
deleteInProgressApp,
|
||||
deleteInProgressAppOpts,
|
||||
] = useAppDeleteFailedInstallationMutation({
|
||||
onCompleted: data => {
|
||||
if (!data?.appDeleteFailedInstallation?.errors?.length) {
|
||||
removeAppNotify();
|
||||
appsInProgressRefetch();
|
||||
closeModal();
|
||||
}
|
||||
},
|
||||
});
|
||||
const [deleteInProgressApp, deleteInProgressAppOpts] =
|
||||
useAppDeleteFailedInstallationMutation({
|
||||
onCompleted: data => {
|
||||
if (!data?.appDeleteFailedInstallation?.errors?.length) {
|
||||
removeAppNotify();
|
||||
appsInProgressRefetch();
|
||||
closeModal();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const appsInProgress = appsInProgressData?.appsInstallations || [];
|
||||
|
|
|
@ -2,7 +2,7 @@ import {
|
|||
attributeAddUrl,
|
||||
AttributeListUrlSortField,
|
||||
} from "@dashboard/attributes/urls";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import FilterBar from "@dashboard/components/FilterBar";
|
||||
import { configurationMenuUrl } from "@dashboard/configuration";
|
||||
|
@ -12,8 +12,6 @@ import { Card } from "@material-ui/core";
|
|||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import Container from "../../../components/Container";
|
||||
import PageHeader from "../../../components/PageHeader";
|
||||
import {
|
||||
FilterPageProps,
|
||||
ListActions,
|
||||
|
@ -55,11 +53,11 @@ const AttributeListPage: React.FC<AttributeListPageProps> = ({
|
|||
const structure = createFilterStructure(intl, filterOpts);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Backlink href={configurationMenuUrl}>
|
||||
<FormattedMessage {...sectionNames.configuration} />
|
||||
</Backlink>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.attributes)}>
|
||||
<>
|
||||
<TopNav
|
||||
href={configurationMenuUrl}
|
||||
title={intl.formatMessage(sectionNames.attributes)}
|
||||
>
|
||||
<Button
|
||||
href={attributeAddUrl()}
|
||||
variant="primary"
|
||||
|
@ -71,7 +69,7 @@ const AttributeListPage: React.FC<AttributeListPageProps> = ({
|
|||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
</TopNav>
|
||||
<Card>
|
||||
<FilterBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
|
@ -96,7 +94,7 @@ const AttributeListPage: React.FC<AttributeListPageProps> = ({
|
|||
/>
|
||||
<AttributeList {...listProps} />
|
||||
</Card>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
AttributeListPage.displayName = "AttributeListPage";
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { attributeListUrl } from "@dashboard/attributes/urls";
|
||||
import { ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES } from "@dashboard/attributes/utils/data";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import { RightSidebar } from "@dashboard/components/AppLayout/RightSidebar";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import Form from "@dashboard/components/Form";
|
||||
import Grid from "@dashboard/components/Grid";
|
||||
import Metadata from "@dashboard/components/Metadata/Metadata";
|
||||
import { MetadataFormData } from "@dashboard/components/Metadata/types";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import { ListSettingsUpdate } from "@dashboard/components/TablePagination";
|
||||
import {
|
||||
|
@ -21,7 +21,7 @@ import {
|
|||
} from "@dashboard/graphql";
|
||||
import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { maybe } from "@dashboard/misc";
|
||||
import { ListSettings, ReorderAction } from "@dashboard/types";
|
||||
import { mapEdgesToItems, mapMetadataItemToInput } from "@dashboard/utils/maps";
|
||||
import useMetadataChangeTrigger from "@dashboard/utils/metadata/useMetadataChangeTrigger";
|
||||
|
@ -173,74 +173,70 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<Container>
|
||||
<Backlink href={attributeListUrl()}>
|
||||
{intl.formatMessage(sectionNames.attributes)}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
<DetailedContent>
|
||||
<TopNav
|
||||
href={attributeListUrl()}
|
||||
title={
|
||||
!attribute
|
||||
attribute === null
|
||||
? intl.formatMessage({
|
||||
id: "8cUEPV",
|
||||
defaultMessage: "Create New Attribute",
|
||||
description: "page title",
|
||||
})
|
||||
: attribute.name
|
||||
: maybe(() => attribute.name)
|
||||
}
|
||||
/>
|
||||
<Grid>
|
||||
<div>
|
||||
<AttributeDetails
|
||||
canChangeType={attribute === null}
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
apiErrors={apiErrors}
|
||||
onChange={change}
|
||||
set={set}
|
||||
errors={errors}
|
||||
setError={setError}
|
||||
clearErrors={clearErrors}
|
||||
/>
|
||||
{ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(
|
||||
data.inputType,
|
||||
) && (
|
||||
<>
|
||||
<CardSpacer />
|
||||
<AttributeValues
|
||||
inputType={data.inputType}
|
||||
disabled={disabled}
|
||||
values={mapEdgesToItems(values) ?? []}
|
||||
onValueAdd={onValueAdd}
|
||||
onValueDelete={onValueDelete}
|
||||
onValueReorder={onValueReorder}
|
||||
onValueUpdate={onValueUpdate}
|
||||
settings={settings}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
pageInfo={pageInfo}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={changeMetadata} />
|
||||
</div>
|
||||
<div>
|
||||
<AttributeOrganization
|
||||
canChangeType={attribute === null}
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<AttributeProperties
|
||||
data={data}
|
||||
errors={apiErrors}
|
||||
disabled={disabled}
|
||||
onChange={change}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
></TopNav>
|
||||
<Content>
|
||||
<AttributeDetails
|
||||
canChangeType={attribute === null}
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
apiErrors={apiErrors}
|
||||
onChange={change}
|
||||
set={set}
|
||||
errors={errors}
|
||||
setError={setError}
|
||||
clearErrors={clearErrors}
|
||||
/>
|
||||
{ATTRIBUTE_TYPES_WITH_DEDICATED_VALUES.includes(
|
||||
data.inputType,
|
||||
) && (
|
||||
<>
|
||||
<CardSpacer />
|
||||
<AttributeValues
|
||||
inputType={data.inputType}
|
||||
disabled={disabled}
|
||||
values={mapEdgesToItems(values)}
|
||||
onValueAdd={onValueAdd}
|
||||
onValueDelete={onValueDelete}
|
||||
onValueReorder={onValueReorder}
|
||||
onValueUpdate={onValueUpdate}
|
||||
settings={settings}
|
||||
onUpdateListSettings={onUpdateListSettings}
|
||||
pageInfo={pageInfo}
|
||||
onNextPage={onNextPage}
|
||||
onPreviousPage={onPreviousPage}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={changeMetadata} />
|
||||
</Content>
|
||||
<RightSidebar>
|
||||
<AttributeOrganization
|
||||
canChangeType={attribute === null}
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<AttributeProperties
|
||||
data={data}
|
||||
errors={apiErrors}
|
||||
disabled={disabled}
|
||||
onChange={change}
|
||||
/>
|
||||
</RightSidebar>
|
||||
<Savebar
|
||||
disabled={!!isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
|
@ -248,7 +244,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
|
|||
onSubmit={submit}
|
||||
onDelete={attribute === null ? undefined : onDelete}
|
||||
/>
|
||||
</Container>
|
||||
</DetailedContent>
|
||||
{children(data)}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -62,7 +62,7 @@ const AttributeValueEditDialog: React.FC<AttributeValueEditDialogProps> = ({
|
|||
|
||||
return (
|
||||
<Dialog onClose={onClose} open={open} fullWidth maxWidth="sm">
|
||||
<DialogTitle>
|
||||
<DialogTitle disableTypography>
|
||||
{attributeValue === null ? (
|
||||
<FormattedMessage
|
||||
id="PqMbma"
|
||||
|
|
|
@ -8,6 +8,7 @@ import { CategoryDetailsFragment } from "@dashboard/graphql";
|
|||
import { commonMessages } from "@dashboard/intl";
|
||||
import { Card, CardContent, TextField } from "@material-ui/core";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { vars } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
@ -26,7 +27,7 @@ const useStyles = makeStyles(
|
|||
},
|
||||
imageContainer: {
|
||||
background: "#ffffff",
|
||||
border: "1px solid #eaeaea",
|
||||
border: `1px solid ${vars.colors.border.neutralPlain}`,
|
||||
borderRadius: theme.spacing(),
|
||||
height: 148,
|
||||
justifySelf: "start",
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { CardSpacer } from "@dashboard/components/CardSpacer";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import Metadata from "@dashboard/components/Metadata";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import SeoForm from "@dashboard/components/SeoForm";
|
||||
import { ProductErrorFragment } from "@dashboard/graphql";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
|
@ -36,52 +36,52 @@ export const CategoryCreatePage: React.FC<CategoryCreatePageProps> = ({
|
|||
return (
|
||||
<CategoryCreateForm onSubmit={onSubmit} disabled={disabled}>
|
||||
{({ data, change, handlers, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink href={backUrl}>
|
||||
{intl.formatMessage(sectionNames.categories)}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
<DetailedContent>
|
||||
<TopNav
|
||||
href={backUrl}
|
||||
title={intl.formatMessage({
|
||||
id: "cgsY/X",
|
||||
defaultMessage: "Create New Category",
|
||||
description: "page header",
|
||||
})}
|
||||
/>
|
||||
<div>
|
||||
<CategoryDetailsForm
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<SeoForm
|
||||
allowEmptySlug={true}
|
||||
helperText={intl.formatMessage({
|
||||
id: "wQdR8M",
|
||||
defaultMessage:
|
||||
"Add search engine title and description to make this category easier to find",
|
||||
})}
|
||||
slug={data.slug}
|
||||
slugPlaceholder={data.name}
|
||||
title={data.seoTitle}
|
||||
titlePlaceholder={data.name}
|
||||
description={data.seoDescription}
|
||||
descriptionPlaceholder={data.name}
|
||||
loading={disabled}
|
||||
onChange={change}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
<Savebar
|
||||
onCancel={() => navigate(backUrl)}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
disabled={isSaveDisabled}
|
||||
/>
|
||||
</div>
|
||||
</Container>
|
||||
<Content>
|
||||
<Box height="100vh" __marginBottom="auto">
|
||||
<CategoryDetailsForm
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<SeoForm
|
||||
allowEmptySlug={true}
|
||||
helperText={intl.formatMessage({
|
||||
id: "wQdR8M",
|
||||
defaultMessage:
|
||||
"Add search engine title and description to make this category easier to find",
|
||||
})}
|
||||
slug={data.slug}
|
||||
slugPlaceholder={data.name}
|
||||
title={data.seoTitle}
|
||||
titlePlaceholder={data.name}
|
||||
description={data.seoDescription}
|
||||
descriptionPlaceholder={data.name}
|
||||
loading={disabled}
|
||||
onChange={change}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
</Box>
|
||||
</Content>
|
||||
<Savebar
|
||||
onCancel={() => navigate(backUrl)}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
disabled={isSaveDisabled}
|
||||
/>
|
||||
</DetailedContent>
|
||||
)}
|
||||
</CategoryCreateForm>
|
||||
);
|
||||
|
|
|
@ -41,7 +41,7 @@ const CategoryDeleteDialog: React.FC<CategoryDeleteDialogProps> = props => {
|
|||
|
||||
return (
|
||||
<Dialog onClose={onClose} open={open}>
|
||||
<DialogTitle>
|
||||
<DialogTitle disableTypography>
|
||||
<FormattedMessage
|
||||
id="xo5UIb"
|
||||
defaultMessage="Delete category"
|
||||
|
|
|
@ -2,9 +2,8 @@ import {
|
|||
categoryAddUrl,
|
||||
CategoryListUrlSortField,
|
||||
} from "@dashboard/categories/urls";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import SearchBar from "@dashboard/components/SearchBar";
|
||||
import { CategoryFragment } from "@dashboard/graphql";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
|
@ -53,8 +52,8 @@ export const CategoryListPage: React.FC<CategoryTableProps> = ({
|
|||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.categories)}>
|
||||
<>
|
||||
<TopNav title={intl.formatMessage(sectionNames.categories)}>
|
||||
<Button
|
||||
variant="primary"
|
||||
href={categoryAddUrl()}
|
||||
|
@ -66,7 +65,7 @@ export const CategoryListPage: React.FC<CategoryTableProps> = ({
|
|||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
</TopNav>
|
||||
<Card>
|
||||
<SearchBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
|
@ -101,7 +100,7 @@ export const CategoryListPage: React.FC<CategoryTableProps> = ({
|
|||
{...listProps}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
CategoryListPage.displayName = "CategoryListPage";
|
||||
|
|
|
@ -3,22 +3,22 @@ import {
|
|||
categoryListUrl,
|
||||
categoryUrl,
|
||||
} from "@dashboard/categories/urls";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import { CardSpacer } from "@dashboard/components/CardSpacer";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import Metadata from "@dashboard/components/Metadata/Metadata";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import SeoForm from "@dashboard/components/SeoForm";
|
||||
import { Tab, TabContainer } from "@dashboard/components/Tab";
|
||||
import { CategoryDetailsQuery, ProductErrorFragment } from "@dashboard/graphql";
|
||||
import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { Card } from "@material-ui/core";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import { sprinkles } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
@ -91,127 +91,126 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
|||
disabled={disabled}
|
||||
>
|
||||
{({ data, change, handlers, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink href={backHref}>
|
||||
{intl.formatMessage(sectionNames.categories)}
|
||||
</Backlink>
|
||||
<PageHeader title={category?.name} />
|
||||
<CategoryDetailsForm
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<CategoryBackground
|
||||
data={data}
|
||||
onImageUpload={onImageUpload}
|
||||
onImageDelete={onImageDelete}
|
||||
image={maybe(() => category.backgroundImage)}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<SeoForm
|
||||
helperText={intl.formatMessage({
|
||||
id: "wQdR8M",
|
||||
defaultMessage:
|
||||
"Add search engine title and description to make this category easier to find",
|
||||
})}
|
||||
errors={errors}
|
||||
title={data.seoTitle}
|
||||
titlePlaceholder={data.name}
|
||||
description={data.seoDescription}
|
||||
descriptionPlaceholder={data.name}
|
||||
slug={data.slug}
|
||||
slugPlaceholder={data.name}
|
||||
loading={!category}
|
||||
onChange={change}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
<CardSpacer />
|
||||
<TabContainer>
|
||||
<CategoriesTab
|
||||
isActive={currentTab === CategoryPageTab.categories}
|
||||
changeTab={changeTab}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="JDz5h8"
|
||||
defaultMessage="Subcategories"
|
||||
description="number of subcategories in category"
|
||||
/>
|
||||
</CategoriesTab>
|
||||
<ProductsTab
|
||||
testId="products-tab"
|
||||
isActive={currentTab === CategoryPageTab.products}
|
||||
changeTab={changeTab}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="V+fkAO"
|
||||
defaultMessage="Products"
|
||||
description="number of products in category"
|
||||
/>
|
||||
</ProductsTab>
|
||||
</TabContainer>
|
||||
<CardSpacer />
|
||||
{currentTab === CategoryPageTab.categories && (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
id: "NivJal",
|
||||
defaultMessage: "All Subcategories",
|
||||
description: "section header",
|
||||
})}
|
||||
toolbar={
|
||||
<Button
|
||||
variant="tertiary"
|
||||
href={categoryAddUrl(categoryId)}
|
||||
data-test-id="create-subcategory"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="UycVMp"
|
||||
defaultMessage="Create subcategory"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<CategoryList
|
||||
categories={subcategories}
|
||||
<DetailedContent>
|
||||
<TopNav href={backHref} title={category?.name} />
|
||||
<Content>
|
||||
<CategoryDetailsForm
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<CategoryBackground
|
||||
data={data}
|
||||
onImageUpload={onImageUpload}
|
||||
onImageDelete={onImageDelete}
|
||||
image={maybe(() => category.backgroundImage)}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<SeoForm
|
||||
helperText={intl.formatMessage({
|
||||
id: "wQdR8M",
|
||||
defaultMessage:
|
||||
"Add search engine title and description to make this category easier to find",
|
||||
})}
|
||||
errors={errors}
|
||||
title={data.seoTitle}
|
||||
titlePlaceholder={data.name}
|
||||
description={data.seoDescription}
|
||||
descriptionPlaceholder={data.name}
|
||||
slug={data.slug}
|
||||
slugPlaceholder={data.name}
|
||||
loading={!category}
|
||||
onChange={change}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
<CardSpacer />
|
||||
<TabContainer className={sprinkles({ paddingX: 9 })}>
|
||||
<CategoriesTab
|
||||
isActive={currentTab === CategoryPageTab.categories}
|
||||
changeTab={changeTab}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="JDz5h8"
|
||||
defaultMessage="Subcategories"
|
||||
description="number of subcategories in category"
|
||||
/>
|
||||
</CategoriesTab>
|
||||
<ProductsTab
|
||||
testId="products-tab"
|
||||
isActive={currentTab === CategoryPageTab.products}
|
||||
changeTab={changeTab}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="V+fkAO"
|
||||
defaultMessage="Products"
|
||||
description="number of products in category"
|
||||
/>
|
||||
</ProductsTab>
|
||||
</TabContainer>
|
||||
<CardSpacer />
|
||||
{currentTab === CategoryPageTab.categories && (
|
||||
<Card>
|
||||
<CardTitle
|
||||
title={intl.formatMessage({
|
||||
id: "NivJal",
|
||||
defaultMessage: "All Subcategories",
|
||||
description: "section header",
|
||||
})}
|
||||
toolbar={
|
||||
<Button
|
||||
variant="tertiary"
|
||||
href={categoryAddUrl(categoryId)}
|
||||
data-test-id="create-subcategory"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="UycVMp"
|
||||
defaultMessage="Create subcategory"
|
||||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<CategoryList
|
||||
categories={subcategories}
|
||||
disabled={disabled}
|
||||
isChecked={isChecked}
|
||||
isRoot={false}
|
||||
selected={selected}
|
||||
sort={undefined}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={subcategoryListToolbar}
|
||||
onSort={() => undefined}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
{currentTab === CategoryPageTab.products && (
|
||||
<CategoryProducts
|
||||
categoryId={category?.id}
|
||||
categoryName={category?.name}
|
||||
products={products}
|
||||
disabled={disabled}
|
||||
isChecked={isChecked}
|
||||
isRoot={false}
|
||||
selected={selected}
|
||||
sort={undefined}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
toolbar={subcategoryListToolbar}
|
||||
onSort={() => undefined}
|
||||
selected={selected}
|
||||
isChecked={isChecked}
|
||||
toolbar={productListToolbar}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
{currentTab === CategoryPageTab.products && (
|
||||
<CategoryProducts
|
||||
categoryId={category?.id}
|
||||
categoryName={category?.name}
|
||||
products={products}
|
||||
disabled={disabled}
|
||||
toggle={toggle}
|
||||
toggleAll={toggleAll}
|
||||
selected={selected}
|
||||
isChecked={isChecked}
|
||||
toolbar={productListToolbar}
|
||||
)}
|
||||
<Savebar
|
||||
onCancel={() => navigate(backHref)}
|
||||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
disabled={isSaveDisabled}
|
||||
/>
|
||||
)}
|
||||
<Savebar
|
||||
onCancel={() => navigate(backHref)}
|
||||
onDelete={onDelete}
|
||||
onSubmit={submit}
|
||||
state={saveButtonBarState}
|
||||
disabled={isSaveDisabled}
|
||||
/>
|
||||
</Container>
|
||||
</Content>
|
||||
</DetailedContent>
|
||||
)}
|
||||
</CategoryUpdateForm>
|
||||
);
|
||||
|
|
|
@ -3,9 +3,12 @@ import ShippingZones from "@dashboard/channels/components/ShippingZones";
|
|||
import Warehouses from "@dashboard/channels/components/Warehouses";
|
||||
import { channelsListUrl } from "@dashboard/channels/urls";
|
||||
import { validateChannelFormData } from "@dashboard/channels/validation";
|
||||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import { RightSidebar } from "@dashboard/components/AppLayout/RightSidebar";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import Form from "@dashboard/components/Form";
|
||||
import Grid from "@dashboard/components/Grid";
|
||||
import RequirePermissions from "@dashboard/components/RequirePermissions";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import { SingleAutocompleteChoiceType } from "@dashboard/components/SingleAutocompleteSelectField";
|
||||
|
@ -30,6 +33,7 @@ import createSingleAutocompleteSelectHandler from "@dashboard/utils/handlers/sin
|
|||
import { mapCountriesToChoices } from "@dashboard/utils/maps";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import React, { useState } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { ChannelForm, FormData } from "../../components/ChannelForm";
|
||||
import { ChannelStatus } from "../../components/ChannelStatus/ChannelStatus";
|
||||
|
@ -43,7 +47,7 @@ import {
|
|||
import { ChannelShippingZones, ChannelWarehouses } from "./types";
|
||||
|
||||
export interface ChannelDetailsPageProps<
|
||||
TErrors extends ChannelErrorFragment[]
|
||||
TErrors extends ChannelErrorFragment[],
|
||||
> {
|
||||
channel?: ChannelDetailsFragment;
|
||||
currencyCodes?: SingleAutocompleteChoiceType[];
|
||||
|
@ -67,7 +71,7 @@ export interface ChannelDetailsPageProps<
|
|||
searchWarehouses: (query: string) => void;
|
||||
}
|
||||
|
||||
const ChannelDetailsPage = function<TErrors extends ChannelErrorFragment[]>({
|
||||
const ChannelDetailsPage = function <TErrors extends ChannelErrorFragment[]>({
|
||||
channel,
|
||||
currencyCodes,
|
||||
disabled,
|
||||
|
@ -90,16 +94,15 @@ const ChannelDetailsPage = function<TErrors extends ChannelErrorFragment[]>({
|
|||
countries,
|
||||
}: ChannelDetailsPageProps<TErrors>) {
|
||||
const navigate = useNavigator();
|
||||
const intl = useIntl();
|
||||
|
||||
const [validationErrors, setValidationErrors] = useState<
|
||||
ChannelErrorFragment[]
|
||||
>([]);
|
||||
|
||||
const [selectedCurrencyCode, setSelectedCurrencyCode] = useState("");
|
||||
const [
|
||||
selectedCountryDisplayName,
|
||||
setSelectedCountryDisplayName,
|
||||
] = useStateFromProps(channel?.defaultCountry.country || "");
|
||||
const [selectedCountryDisplayName, setSelectedCountryDisplayName] =
|
||||
useStateFromProps(channel?.defaultCountry.country || "");
|
||||
|
||||
const countryChoices = mapCountriesToChoices(countries || []);
|
||||
|
||||
|
@ -160,11 +163,12 @@ const ChannelDetailsPage = function<TErrors extends ChannelErrorFragment[]>({
|
|||
setSelectedCurrencyCode,
|
||||
currencyCodes,
|
||||
);
|
||||
const handleDefaultCountrySelect = createSingleAutocompleteSelectHandler(
|
||||
change,
|
||||
setSelectedCountryDisplayName,
|
||||
countryChoices,
|
||||
);
|
||||
const handleDefaultCountrySelect =
|
||||
createSingleAutocompleteSelectHandler(
|
||||
change,
|
||||
setSelectedCountryDisplayName,
|
||||
countryChoices,
|
||||
);
|
||||
|
||||
const addShippingZone = createShippingZoneAddHandler(
|
||||
data,
|
||||
|
@ -194,79 +198,88 @@ const ChannelDetailsPage = function<TErrors extends ChannelErrorFragment[]>({
|
|||
const allErrors = [...errors, ...validationErrors];
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<div>
|
||||
<ChannelForm
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
currencyCodes={currencyCodes}
|
||||
countries={countryChoices}
|
||||
selectedCurrencyCode={selectedCurrencyCode}
|
||||
selectedCountryDisplayName={selectedCountryDisplayName}
|
||||
onChange={change}
|
||||
onCurrencyCodeChange={handleCurrencyCodeSelect}
|
||||
onDefaultCountryChange={handleDefaultCountrySelect}
|
||||
errors={allErrors}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
{!!updateChannelStatus && (
|
||||
<>
|
||||
<ChannelStatus
|
||||
isActive={channel?.isActive}
|
||||
disabled={disabledStatus}
|
||||
updateChannelStatus={updateChannelStatus}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</>
|
||||
)}
|
||||
<RequirePermissions
|
||||
requiredPermissions={[PermissionEnum.MANAGE_SHIPPING]}
|
||||
>
|
||||
<ShippingZones
|
||||
shippingZonesChoices={getFilteredShippingZonesChoices(
|
||||
data.shippingZonesToDisplay,
|
||||
)}
|
||||
shippingZones={data.shippingZonesToDisplay}
|
||||
addShippingZone={addShippingZone}
|
||||
removeShippingZone={removeShippingZone}
|
||||
searchShippingZones={searchShippingZones}
|
||||
fetchMoreShippingZones={fetchMoreShippingZones}
|
||||
totalCount={allShippingZonesCount}
|
||||
loading={disabled}
|
||||
<DetailedContent>
|
||||
<TopNav
|
||||
href={channelsListUrl()}
|
||||
title={
|
||||
channel?.name ||
|
||||
intl.formatMessage({
|
||||
id: "DnghuS",
|
||||
defaultMessage: "New Channel",
|
||||
description: "channel create",
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Content>
|
||||
<ChannelForm
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
currencyCodes={currencyCodes}
|
||||
countries={countryChoices}
|
||||
selectedCurrencyCode={selectedCurrencyCode}
|
||||
selectedCountryDisplayName={selectedCountryDisplayName}
|
||||
onChange={change}
|
||||
onCurrencyCodeChange={handleCurrencyCodeSelect}
|
||||
onDefaultCountryChange={handleDefaultCountrySelect}
|
||||
errors={allErrors}
|
||||
/>
|
||||
</Content>
|
||||
<RightSidebar>
|
||||
{!!updateChannelStatus && (
|
||||
<>
|
||||
<ChannelStatus
|
||||
isActive={channel?.isActive}
|
||||
disabled={disabledStatus}
|
||||
updateChannelStatus={updateChannelStatus}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</RequirePermissions>
|
||||
<RequirePermissions
|
||||
oneOfPermissions={[
|
||||
PermissionEnum.MANAGE_SHIPPING,
|
||||
PermissionEnum.MANAGE_ORDERS,
|
||||
PermissionEnum.MANAGE_PRODUCTS,
|
||||
]}
|
||||
>
|
||||
<Warehouses
|
||||
warehousesChoices={getFilteredWarehousesChoices(
|
||||
data.warehousesToDisplay,
|
||||
)}
|
||||
warehouses={data.warehousesToDisplay}
|
||||
addWarehouse={addWarehouse}
|
||||
removeWarehouse={removeWarehouse}
|
||||
searchWarehouses={searchWarehouses}
|
||||
fetchMoreWarehouses={fetchMoreWarehouses}
|
||||
totalCount={allWarehousesCount}
|
||||
reorderWarehouses={reorderWarehouse}
|
||||
loading={disabled}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</RequirePermissions>
|
||||
<ChannelAllocationStrategy
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
onChange={change}
|
||||
</>
|
||||
)}
|
||||
<RequirePermissions
|
||||
requiredPermissions={[PermissionEnum.MANAGE_SHIPPING]}
|
||||
>
|
||||
<ShippingZones
|
||||
shippingZonesChoices={getFilteredShippingZonesChoices(
|
||||
data.shippingZonesToDisplay,
|
||||
)}
|
||||
shippingZones={data.shippingZonesToDisplay}
|
||||
addShippingZone={addShippingZone}
|
||||
removeShippingZone={removeShippingZone}
|
||||
searchShippingZones={searchShippingZones}
|
||||
fetchMoreShippingZones={fetchMoreShippingZones}
|
||||
totalCount={allShippingZonesCount}
|
||||
loading={disabled}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
<CardSpacer />
|
||||
</RequirePermissions>
|
||||
<RequirePermissions
|
||||
oneOfPermissions={[
|
||||
PermissionEnum.MANAGE_SHIPPING,
|
||||
PermissionEnum.MANAGE_ORDERS,
|
||||
PermissionEnum.MANAGE_PRODUCTS,
|
||||
]}
|
||||
>
|
||||
<Warehouses
|
||||
warehousesChoices={getFilteredWarehousesChoices(
|
||||
data.warehousesToDisplay,
|
||||
)}
|
||||
warehouses={data.warehousesToDisplay}
|
||||
addWarehouse={addWarehouse}
|
||||
removeWarehouse={removeWarehouse}
|
||||
searchWarehouses={searchWarehouses}
|
||||
fetchMoreWarehouses={fetchMoreWarehouses}
|
||||
totalCount={allWarehousesCount}
|
||||
reorderWarehouses={reorderWarehouse}
|
||||
loading={disabled}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</RequirePermissions>
|
||||
<ChannelAllocationStrategy
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
onChange={change}
|
||||
/>
|
||||
</RightSidebar>
|
||||
<Savebar
|
||||
onCancel={() => navigate(channelsListUrl())}
|
||||
onSubmit={submit}
|
||||
|
@ -274,7 +287,7 @@ const ChannelDetailsPage = function<TErrors extends ChannelErrorFragment[]>({
|
|||
state={saveButtonBarState}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</>
|
||||
</DetailedContent>
|
||||
);
|
||||
}}
|
||||
</Form>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { channelAddUrl, channelUrl } from "@dashboard/channels/urls";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import { LimitsInfo } from "@dashboard/components/AppLayout/LimitsInfo";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import LimitReachedAlert from "@dashboard/components/LimitReachedAlert";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import ResponsiveTable from "@dashboard/components/ResponsiveTable";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import { TableButtonWrapper } from "@dashboard/components/TableButtonWrapper/TableButtonWrapper";
|
||||
|
@ -40,26 +39,10 @@ export const ChannelsListPage: React.FC<ChannelsListPageProps> = ({
|
|||
const limitReached = isLimitReached(limits, "channels");
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Backlink href={configurationMenuUrl}>
|
||||
{intl.formatMessage(sectionNames.configuration)}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
<>
|
||||
<TopNav
|
||||
href={configurationMenuUrl}
|
||||
title={intl.formatMessage(sectionNames.channels)}
|
||||
limitText={
|
||||
hasLimits(limits, "channels") &&
|
||||
intl.formatMessage(
|
||||
{
|
||||
id: "rZMT44",
|
||||
defaultMessage: "{count}/{max} channels used",
|
||||
description: "created channels counter",
|
||||
},
|
||||
{
|
||||
count: limits.currentUsage.channels,
|
||||
max: limits.allowedUsage.channels,
|
||||
},
|
||||
)
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={limitReached}
|
||||
|
@ -73,7 +56,22 @@ export const ChannelsListPage: React.FC<ChannelsListPageProps> = ({
|
|||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
{hasLimits(limits, "channels") && (
|
||||
<LimitsInfo
|
||||
text={intl.formatMessage(
|
||||
{
|
||||
id: "rZMT44",
|
||||
defaultMessage: "{count}/{max} channels used",
|
||||
description: "created channels counter",
|
||||
},
|
||||
{
|
||||
count: limits.currentUsage.channels,
|
||||
max: limits.allowedUsage.channels,
|
||||
},
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</TopNav>
|
||||
{limitReached && (
|
||||
<LimitReachedAlert
|
||||
title={intl.formatMessage({
|
||||
|
@ -156,7 +154,7 @@ export const ChannelsListPage: React.FC<ChannelsListPageProps> = ({
|
|||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</Card>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import { FormData } from "@dashboard/channels/components/ChannelForm/ChannelForm";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import { WindowTitle } from "@dashboard/components/WindowTitle";
|
||||
import {
|
||||
ChannelCreateMutation,
|
||||
|
@ -14,7 +11,6 @@ import useNavigator from "@dashboard/hooks/useNavigator";
|
|||
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||
import { getDefaultNotifierSuccessErrorData } from "@dashboard/hooks/useNotifier/utils";
|
||||
import useShop from "@dashboard/hooks/useShop";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { extractMutationErrors } from "@dashboard/misc";
|
||||
import getChannelsErrorMessage from "@dashboard/utils/errors/channels";
|
||||
import currencyCodes from "currency-codes";
|
||||
|
@ -22,7 +18,7 @@ import React from "react";
|
|||
import { useIntl } from "react-intl";
|
||||
|
||||
import ChannelDetailsPage from "../../pages/ChannelDetailsPage";
|
||||
import { channelPath, channelsListUrl } from "../../urls";
|
||||
import { channelPath } from "../../urls";
|
||||
import { calculateItemsOrderMoves } from "../ChannelDetails/handlers";
|
||||
import { useShippingZones } from "../ChannelDetails/useShippingZones";
|
||||
import { useWarehouses } from "../ChannelDetails/useWarehouses";
|
||||
|
@ -146,17 +142,7 @@ export const ChannelCreateView = ({}) => {
|
|||
description: "window title",
|
||||
})}
|
||||
/>
|
||||
<Container>
|
||||
<Backlink href={channelsListUrl()}>
|
||||
{intl.formatMessage(sectionNames.channels)}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
title={intl.formatMessage({
|
||||
id: "DnghuS",
|
||||
defaultMessage: "New Channel",
|
||||
description: "channel create",
|
||||
})}
|
||||
/>
|
||||
<>
|
||||
<ChannelDetailsPage
|
||||
allShippingZonesCount={
|
||||
shippingZonesCountData?.shippingZones?.totalCount
|
||||
|
@ -186,7 +172,7 @@ export const ChannelCreateView = ({}) => {
|
|||
saveButtonBarState={createChannelOpts.status}
|
||||
countries={shop?.countries || []}
|
||||
/>
|
||||
</Container>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import ChannelDeleteDialog from "@dashboard/channels/components/ChannelDeleteDialog";
|
||||
import { FormData } from "@dashboard/channels/components/ChannelForm/ChannelForm";
|
||||
import { getChannelsCurrencyChoices } from "@dashboard/channels/utils";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import { WindowTitle } from "@dashboard/components/WindowTitle";
|
||||
import {
|
||||
ChannelDeleteMutation,
|
||||
|
@ -22,7 +19,6 @@ import useNavigator from "@dashboard/hooks/useNavigator";
|
|||
import useNotifier from "@dashboard/hooks/useNotifier";
|
||||
import { getDefaultNotifierSuccessErrorData } from "@dashboard/hooks/useNotifier/utils";
|
||||
import useShop from "@dashboard/hooks/useShop";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { extractMutationErrors } from "@dashboard/misc";
|
||||
import getChannelsErrorMessage from "@dashboard/utils/errors/channels";
|
||||
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
|
||||
|
@ -230,54 +226,48 @@ export const ChannelDetails: React.FC<ChannelDetailsProps> = ({
|
|||
description: "window title",
|
||||
})}
|
||||
/>
|
||||
<Container>
|
||||
<Backlink href={channelsListUrl()}>
|
||||
{intl.formatMessage(sectionNames.channels)}
|
||||
</Backlink>
|
||||
<PageHeader title={data?.channel?.name} />
|
||||
<ChannelDetailsPage
|
||||
channelShippingZones={channelShippingZones}
|
||||
allShippingZonesCount={
|
||||
shippingZonesCountData?.shippingZones?.totalCount
|
||||
}
|
||||
searchShippingZones={searchShippingZones}
|
||||
searchShippingZonesData={searchShippingZonesResult.data}
|
||||
fetchMoreShippingZones={getSearchFetchMoreProps(
|
||||
searchShippingZonesResult,
|
||||
fetchMoreShippingZones,
|
||||
)}
|
||||
channelWarehouses={channelWarehouses}
|
||||
allWarehousesCount={warehousesCountData?.warehouses?.totalCount}
|
||||
searchWarehouses={searchWarehouses}
|
||||
searchWarehousesData={searchWarehousesResult.data}
|
||||
fetchMoreWarehouses={getSearchFetchMoreProps(
|
||||
searchWarehousesResult,
|
||||
fetchMoreWarehouses,
|
||||
)}
|
||||
channel={data?.channel}
|
||||
disabled={
|
||||
updateChannelOpts.loading ||
|
||||
reorderChannelWarehousesOpts.loading ||
|
||||
loading ||
|
||||
shippingZonesCountLoading ||
|
||||
warehousesCountLoading ||
|
||||
channelsShippingZonesLoading
|
||||
}
|
||||
disabledStatus={
|
||||
activateChannelOpts.loading || deactivateChannelOpts.loading
|
||||
}
|
||||
errors={updateChannelOpts?.data?.channelUpdate?.errors || []}
|
||||
onDelete={() => openModal("remove")}
|
||||
onSubmit={handleSubmit}
|
||||
updateChannelStatus={() =>
|
||||
data?.channel?.isActive
|
||||
? deactivateChannel({ variables: { id } })
|
||||
: activateChannel({ variables: { id } })
|
||||
}
|
||||
saveButtonBarState={updateChannelOpts.status}
|
||||
countries={shop?.countries || []}
|
||||
/>
|
||||
</Container>
|
||||
<ChannelDetailsPage
|
||||
channelShippingZones={channelShippingZones}
|
||||
allShippingZonesCount={
|
||||
shippingZonesCountData?.shippingZones?.totalCount
|
||||
}
|
||||
searchShippingZones={searchShippingZones}
|
||||
searchShippingZonesData={searchShippingZonesResult.data}
|
||||
fetchMoreShippingZones={getSearchFetchMoreProps(
|
||||
searchShippingZonesResult,
|
||||
fetchMoreShippingZones,
|
||||
)}
|
||||
channelWarehouses={channelWarehouses}
|
||||
allWarehousesCount={warehousesCountData?.warehouses?.totalCount}
|
||||
searchWarehouses={searchWarehouses}
|
||||
searchWarehousesData={searchWarehousesResult.data}
|
||||
fetchMoreWarehouses={getSearchFetchMoreProps(
|
||||
searchWarehousesResult,
|
||||
fetchMoreWarehouses,
|
||||
)}
|
||||
channel={data?.channel}
|
||||
disabled={
|
||||
updateChannelOpts.loading ||
|
||||
reorderChannelWarehousesOpts.loading ||
|
||||
loading ||
|
||||
shippingZonesCountLoading ||
|
||||
warehousesCountLoading ||
|
||||
channelsShippingZonesLoading
|
||||
}
|
||||
disabledStatus={
|
||||
activateChannelOpts.loading || deactivateChannelOpts.loading
|
||||
}
|
||||
errors={updateChannelOpts?.data?.channelUpdate?.errors || []}
|
||||
onDelete={() => openModal("remove")}
|
||||
onSubmit={handleSubmit}
|
||||
updateChannelStatus={() =>
|
||||
data?.channel?.isActive
|
||||
? deactivateChannel({ variables: { id } })
|
||||
: activateChannel({ variables: { id } })
|
||||
}
|
||||
saveButtonBarState={updateChannelOpts.status}
|
||||
countries={shop?.countries || []}
|
||||
/>
|
||||
<ChannelDeleteDialog
|
||||
channelsChoices={channelsChoices}
|
||||
hasOrders={data?.channel?.hasOrders}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { ChannelCollectionData } from "@dashboard/channels/utils";
|
||||
import { collectionListUrl } from "@dashboard/collections/urls";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import { RightSidebar } from "@dashboard/components/AppLayout/RightSidebar";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { CardSpacer } from "@dashboard/components/CardSpacer";
|
||||
import ChannelsAvailabilityCard from "@dashboard/components/ChannelsAvailabilityCard";
|
||||
import { Container } from "@dashboard/components/Container";
|
||||
import Grid from "@dashboard/components/Grid";
|
||||
import Metadata from "@dashboard/components/Metadata";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import SeoForm from "@dashboard/components/SeoForm";
|
||||
import {
|
||||
|
@ -16,7 +16,6 @@ import {
|
|||
} from "@dashboard/graphql";
|
||||
import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
@ -59,113 +58,109 @@ const CollectionCreatePage: React.FC<CollectionCreatePageProps> = ({
|
|||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, handlers, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink href={collectionListUrl()}>
|
||||
{intl.formatMessage(sectionNames.collections)}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
<DetailedContent>
|
||||
<TopNav
|
||||
href={collectionListUrl()}
|
||||
title={intl.formatMessage({
|
||||
id: "Fxa6xp",
|
||||
defaultMessage: "Add Collection",
|
||||
description: "page header",
|
||||
})}
|
||||
/>
|
||||
<Grid>
|
||||
<div>
|
||||
<CollectionDetails
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<CollectionImage
|
||||
image={
|
||||
data.backgroundImage.url
|
||||
? {
|
||||
__typename: "Image",
|
||||
alt: data.backgroundImageAlt,
|
||||
url: data.backgroundImage.url,
|
||||
}
|
||||
: null
|
||||
}
|
||||
onImageDelete={() =>
|
||||
change({
|
||||
target: {
|
||||
name: "backgroundImage",
|
||||
value: {
|
||||
url: null,
|
||||
value: null,
|
||||
},
|
||||
<Content>
|
||||
<CollectionDetails
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<CollectionImage
|
||||
image={
|
||||
data.backgroundImage.url
|
||||
? {
|
||||
__typename: "Image",
|
||||
alt: data.backgroundImageAlt,
|
||||
url: data.backgroundImage.url,
|
||||
}
|
||||
: null
|
||||
}
|
||||
onImageDelete={() =>
|
||||
change({
|
||||
target: {
|
||||
name: "backgroundImage",
|
||||
value: {
|
||||
url: null,
|
||||
value: null,
|
||||
},
|
||||
} as any)
|
||||
}
|
||||
onImageUpload={file =>
|
||||
change({
|
||||
target: {
|
||||
name: "backgroundImage",
|
||||
value: {
|
||||
url: URL.createObjectURL(file),
|
||||
value: file,
|
||||
},
|
||||
},
|
||||
} as any)
|
||||
}
|
||||
onImageUpload={file =>
|
||||
change({
|
||||
target: {
|
||||
name: "backgroundImage",
|
||||
value: {
|
||||
url: URL.createObjectURL(file),
|
||||
value: file,
|
||||
},
|
||||
} as any)
|
||||
}
|
||||
onChange={change}
|
||||
data={data}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<SeoForm
|
||||
allowEmptySlug={true}
|
||||
description={data.seoDescription}
|
||||
disabled={disabled}
|
||||
descriptionPlaceholder=""
|
||||
helperText={intl.formatMessage({
|
||||
id: "Rj8LxK",
|
||||
defaultMessage:
|
||||
"Add search engine title and description to make this collection easier to find",
|
||||
})}
|
||||
slug={data.slug}
|
||||
slugPlaceholder={data.name}
|
||||
title={data.seoTitle}
|
||||
titlePlaceholder={data.name}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
</div>
|
||||
<div>
|
||||
<ChannelsAvailabilityCard
|
||||
messages={{
|
||||
hiddenLabel: intl.formatMessage({
|
||||
id: "V8FhTt",
|
||||
defaultMessage: "Hidden",
|
||||
description: "collection label",
|
||||
}),
|
||||
},
|
||||
} as any)
|
||||
}
|
||||
onChange={change}
|
||||
data={data}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<SeoForm
|
||||
allowEmptySlug={true}
|
||||
description={data.seoDescription}
|
||||
disabled={disabled}
|
||||
descriptionPlaceholder=""
|
||||
helperText={intl.formatMessage({
|
||||
id: "Rj8LxK",
|
||||
defaultMessage:
|
||||
"Add search engine title and description to make this collection easier to find",
|
||||
})}
|
||||
slug={data.slug}
|
||||
slugPlaceholder={data.name}
|
||||
title={data.seoTitle}
|
||||
titlePlaceholder={data.name}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
</Content>
|
||||
<RightSidebar>
|
||||
<ChannelsAvailabilityCard
|
||||
messages={{
|
||||
hiddenLabel: intl.formatMessage({
|
||||
id: "V8FhTt",
|
||||
defaultMessage: "Hidden",
|
||||
description: "collection label",
|
||||
}),
|
||||
|
||||
visibleLabel: intl.formatMessage({
|
||||
id: "9vQR6c",
|
||||
defaultMessage: "Visible",
|
||||
description: "collection label",
|
||||
}),
|
||||
}}
|
||||
managePermissions={[PermissionEnum.MANAGE_PRODUCTS]}
|
||||
errors={channelsErrors}
|
||||
allChannelsCount={channelsCount}
|
||||
channels={data.channelListings}
|
||||
disabled={disabled}
|
||||
onChange={handlers.changeChannels}
|
||||
openModal={openChannelsModal}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
visibleLabel: intl.formatMessage({
|
||||
id: "9vQR6c",
|
||||
defaultMessage: "Visible",
|
||||
description: "collection label",
|
||||
}),
|
||||
}}
|
||||
managePermissions={[PermissionEnum.MANAGE_PRODUCTS]}
|
||||
errors={channelsErrors}
|
||||
allChannelsCount={channelsCount}
|
||||
channels={data.channelListings}
|
||||
disabled={disabled}
|
||||
onChange={handlers.changeChannels}
|
||||
openModal={openChannelsModal}
|
||||
/>
|
||||
</RightSidebar>
|
||||
<Savebar
|
||||
state={saveButtonBarState}
|
||||
disabled={isSaveDisabled}
|
||||
onCancel={() => navigate(collectionListUrl())}
|
||||
onSubmit={submit}
|
||||
/>
|
||||
</Container>
|
||||
</DetailedContent>
|
||||
)}
|
||||
</CollectionCreateForm>
|
||||
);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { ChannelCollectionData } from "@dashboard/channels/utils";
|
||||
import { collectionListUrl } from "@dashboard/collections/urls";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import { RightSidebar } from "@dashboard/components/AppLayout/RightSidebar";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { CardSpacer } from "@dashboard/components/CardSpacer";
|
||||
import ChannelsAvailabilityCard from "@dashboard/components/ChannelsAvailabilityCard";
|
||||
import { Container } from "@dashboard/components/Container";
|
||||
import Grid from "@dashboard/components/Grid";
|
||||
import Metadata from "@dashboard/components/Metadata/Metadata";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import SeoForm from "@dashboard/components/SeoForm";
|
||||
import {
|
||||
|
@ -17,7 +17,6 @@ import {
|
|||
} from "@dashboard/graphql";
|
||||
import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
@ -76,80 +75,75 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
|
|||
disabled={disabled}
|
||||
>
|
||||
{({ change, data, handlers, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink href={collectionListUrl()}>
|
||||
{intl.formatMessage(sectionNames.collections)}
|
||||
</Backlink>
|
||||
<PageHeader title={collection?.name} />
|
||||
<Grid>
|
||||
<DetailedContent>
|
||||
<TopNav href={collectionListUrl()} title={collection?.name} />
|
||||
<Content>
|
||||
<CollectionDetails
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<CollectionImage
|
||||
data={data}
|
||||
image={collection?.backgroundImage}
|
||||
onImageDelete={onImageDelete}
|
||||
onImageUpload={onImageUpload}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
<CardSpacer />
|
||||
<CollectionProducts
|
||||
disabled={disabled}
|
||||
collection={collection}
|
||||
{...collectionProductsProps}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<SeoForm
|
||||
description={data.seoDescription}
|
||||
disabled={disabled}
|
||||
descriptionPlaceholder=""
|
||||
helperText={intl.formatMessage({
|
||||
id: "Rj8LxK",
|
||||
defaultMessage:
|
||||
"Add search engine title and description to make this collection easier to find",
|
||||
})}
|
||||
errors={errors}
|
||||
slug={data.slug}
|
||||
slugPlaceholder={data.name}
|
||||
title={data.seoTitle}
|
||||
titlePlaceholder={collection?.name}
|
||||
onChange={change}
|
||||
/>
|
||||
</Content>
|
||||
<RightSidebar>
|
||||
<div>
|
||||
<CollectionDetails
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<CollectionImage
|
||||
data={data}
|
||||
image={collection?.backgroundImage}
|
||||
onImageDelete={onImageDelete}
|
||||
onImageUpload={onImageUpload}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||
<CardSpacer />
|
||||
<CollectionProducts
|
||||
disabled={disabled}
|
||||
collection={collection}
|
||||
{...collectionProductsProps}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<SeoForm
|
||||
description={data.seoDescription}
|
||||
disabled={disabled}
|
||||
descriptionPlaceholder=""
|
||||
helperText={intl.formatMessage({
|
||||
id: "Rj8LxK",
|
||||
defaultMessage:
|
||||
"Add search engine title and description to make this collection easier to find",
|
||||
})}
|
||||
errors={errors}
|
||||
slug={data.slug}
|
||||
slugPlaceholder={data.name}
|
||||
title={data.seoTitle}
|
||||
titlePlaceholder={collection?.name}
|
||||
onChange={change}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<ChannelsAvailabilityCard
|
||||
managePermissions={[PermissionEnum.MANAGE_PRODUCTS]}
|
||||
messages={{
|
||||
hiddenLabel: intl.formatMessage({
|
||||
id: "V8FhTt",
|
||||
defaultMessage: "Hidden",
|
||||
description: "collection label",
|
||||
}),
|
||||
<ChannelsAvailabilityCard
|
||||
managePermissions={[PermissionEnum.MANAGE_PRODUCTS]}
|
||||
messages={{
|
||||
hiddenLabel: intl.formatMessage({
|
||||
id: "V8FhTt",
|
||||
defaultMessage: "Hidden",
|
||||
description: "collection label",
|
||||
}),
|
||||
|
||||
visibleLabel: intl.formatMessage({
|
||||
id: "9vQR6c",
|
||||
defaultMessage: "Visible",
|
||||
description: "collection label",
|
||||
}),
|
||||
}}
|
||||
errors={channelsErrors}
|
||||
allChannelsCount={channelsCount}
|
||||
channels={data.channelListings}
|
||||
disabled={disabled}
|
||||
onChange={handlers.changeChannels}
|
||||
openModal={openChannelsModal}
|
||||
/>
|
||||
</div>
|
||||
visibleLabel: intl.formatMessage({
|
||||
id: "9vQR6c",
|
||||
defaultMessage: "Visible",
|
||||
description: "collection label",
|
||||
}),
|
||||
}}
|
||||
errors={channelsErrors}
|
||||
allChannelsCount={channelsCount}
|
||||
channels={data.channelListings}
|
||||
disabled={disabled}
|
||||
onChange={handlers.changeChannels}
|
||||
openModal={openChannelsModal}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
</RightSidebar>
|
||||
<Savebar
|
||||
state={saveButtonBarState}
|
||||
disabled={isSaveDisabled}
|
||||
|
@ -157,7 +151,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
|
|||
onDelete={onCollectionRemove}
|
||||
onSubmit={submit}
|
||||
/>
|
||||
</Container>
|
||||
</DetailedContent>
|
||||
)}
|
||||
</CollectionUpdateForm>
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@ import { CollectionDetailsFragment } from "@dashboard/graphql";
|
|||
import { commonMessages } from "@dashboard/intl";
|
||||
import { Card, CardContent, TextField } from "@material-ui/core";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { vars } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
|
@ -33,7 +34,7 @@ const useStyles = makeStyles(
|
|||
},
|
||||
imageContainer: {
|
||||
background: "#ffffff",
|
||||
border: "1px solid #eaeaea",
|
||||
border: `1px solid ${vars.colors.border.neutralPlain}`,
|
||||
borderRadius: theme.spacing(),
|
||||
height: 148,
|
||||
justifySelf: "start",
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { collectionAddUrl } from "@dashboard/collections/urls";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import { Container } from "@dashboard/components/Container";
|
||||
import { getByName } from "@dashboard/components/Filter/utils";
|
||||
import FilterBar from "@dashboard/components/FilterBar";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import {
|
||||
FilterPageProps,
|
||||
|
@ -52,8 +51,8 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
|
|||
const filterDependency = filterStructure.find(getByName("channel"));
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.collections)}>
|
||||
<>
|
||||
<TopNav title={intl.formatMessage(sectionNames.collections)}>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
variant="primary"
|
||||
|
@ -66,7 +65,7 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
|
|||
description="button"
|
||||
/>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
</TopNav>
|
||||
<Card>
|
||||
<FilterBar
|
||||
allTabLabel={intl.formatMessage({
|
||||
|
@ -97,7 +96,7 @@ const CollectionListPage: React.FC<CollectionListPageProps> = ({
|
|||
{...listProps}
|
||||
/>
|
||||
</Card>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
CollectionListPage.displayName = "CollectionListPage";
|
||||
|
|
|
@ -98,7 +98,7 @@ const AccountPermissions: React.FC<AccountPermissionsProps> = props => {
|
|||
/>
|
||||
{permissionsExceeded && (
|
||||
<>
|
||||
<CardContent>
|
||||
<CardContent style={{ paddingLeft: 0 }}>
|
||||
<Typography variant="body2">
|
||||
{intl.formatMessage({
|
||||
id: "MVU6ol",
|
||||
|
|
|
@ -22,7 +22,7 @@ const ActionDialog: React.FC<ActionDialogProps> = props => {
|
|||
|
||||
return (
|
||||
<Dialog fullWidth onClose={onClose} open={open} maxWidth={maxWidth}>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogTitle disableTypography>{title}</DialogTitle>
|
||||
<DialogContent>{children}</DialogContent>
|
||||
<DialogButtons {...rest} onClose={onClose} variant={variant} />
|
||||
</Dialog>
|
||||
|
|
|
@ -2,23 +2,24 @@ import { ChannelFragment } from "@dashboard/graphql";
|
|||
import { ChannelProps } from "@dashboard/types";
|
||||
import { mapNodeToChoice } from "@dashboard/utils/maps";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { vars } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
import SingleSelectField from "../SingleSelectField";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
{
|
||||
input: {
|
||||
height: 40,
|
||||
},
|
||||
root: {
|
||||
"&& fieldset": {
|
||||
borderColor: theme.palette.divider,
|
||||
borderColor: vars.colors.border.neutralPlain,
|
||||
},
|
||||
marginRight: theme.spacing(2),
|
||||
width: 192,
|
||||
padding: 10,
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "AppChannelSelect",
|
||||
},
|
||||
|
|
|
@ -1,66 +1,24 @@
|
|||
import { useUser } from "@dashboard/auth";
|
||||
import useAppState from "@dashboard/hooks/useAppState";
|
||||
import { isDarkTheme } from "@dashboard/misc";
|
||||
import { LinearProgress, useMediaQuery } from "@material-ui/core";
|
||||
import {
|
||||
SaleorTheme,
|
||||
Sidebar,
|
||||
SidebarDrawer,
|
||||
useActionBar,
|
||||
useBacklink,
|
||||
useTheme,
|
||||
} from "@saleor/macaw-ui";
|
||||
import clsx from "clsx";
|
||||
import { LinearProgress } from "@material-ui/core";
|
||||
import { useActionBar } from "@saleor/macaw-ui";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import useRouter from "use-react-router";
|
||||
|
||||
import Container from "../Container";
|
||||
import Navigator from "../Navigator";
|
||||
import NavigatorButton from "../NavigatorButton/NavigatorButton";
|
||||
import UserChip from "../UserChip";
|
||||
import useAppChannel from "./AppChannelContext";
|
||||
import AppChannelSelect from "./AppChannelSelect";
|
||||
import useMenuStructure from "./menuStructure";
|
||||
import { SidebarLink } from "./SidebarLink";
|
||||
import { useFullSizeStyles, useStyles } from "./styles";
|
||||
import { isMenuActive } from "./utils";
|
||||
import { Sidebar } from "../Sidebar";
|
||||
import { contentMaxWidth } from "./consts";
|
||||
import { useStyles } from "./styles";
|
||||
|
||||
interface AppLayoutProps {
|
||||
children: React.ReactNode;
|
||||
fullSize?: boolean;
|
||||
}
|
||||
|
||||
const AppLayout: React.FC<AppLayoutProps> = ({
|
||||
children,
|
||||
fullSize = false,
|
||||
}) => {
|
||||
const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
|
||||
const classes = useStyles();
|
||||
const fullSizeClasses = useFullSizeStyles();
|
||||
const { themeType, setTheme } = useTheme();
|
||||
const { anchor: appActionAnchor } = useActionBar();
|
||||
const appHeaderAnchor = useBacklink();
|
||||
const { logout, user } = useUser();
|
||||
const intl = useIntl();
|
||||
const [appState] = useAppState();
|
||||
const { location } = useRouter();
|
||||
const [isNavigatorVisible, setNavigatorVisibility] = React.useState(false);
|
||||
const isMdUp = useMediaQuery((theme: SaleorTheme) =>
|
||||
theme.breakpoints.up("md"),
|
||||
);
|
||||
|
||||
const {
|
||||
availableChannels,
|
||||
channel,
|
||||
isPickerActive,
|
||||
setChannel,
|
||||
} = useAppChannel(false);
|
||||
const [menuStructure, handleMenuItemClick] = useMenuStructure(intl, user);
|
||||
const activeMenu = menuStructure.find(menuItem =>
|
||||
isMenuActive(location.pathname, menuItem),
|
||||
)?.id;
|
||||
|
||||
const toggleTheme = () => setTheme(isDarkTheme(themeType) ? "light" : "dark");
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -68,80 +26,45 @@ const AppLayout: React.FC<AppLayoutProps> = ({
|
|||
visible={isNavigatorVisible}
|
||||
setVisibility={setNavigatorVisibility}
|
||||
/>
|
||||
<div className={classes.root}>
|
||||
{isMdUp && (
|
||||
<Sidebar
|
||||
activeId={activeMenu}
|
||||
menuItems={menuStructure}
|
||||
onMenuItemClick={handleMenuItemClick}
|
||||
logoHref="/"
|
||||
linkComponent={SidebarLink}
|
||||
/>
|
||||
<Box display="grid" __gridTemplateColumns="auto 1fr">
|
||||
{appState.loading && (
|
||||
<LinearProgress className={classes.appLoader} color="primary" />
|
||||
)}
|
||||
<div
|
||||
className={clsx(classes.content, {
|
||||
[fullSizeClasses.content]: fullSize,
|
||||
})}
|
||||
<Box
|
||||
height="100vh"
|
||||
borderColor="neutralPlain"
|
||||
borderRightWidth={1}
|
||||
backgroundColor="subdued"
|
||||
borderStyle="solid"
|
||||
position="sticky"
|
||||
top={0}
|
||||
borderLeftWidth={0}
|
||||
borderTopWidth={0}
|
||||
borderBottomWidth={0}
|
||||
>
|
||||
{appState.loading ? (
|
||||
<LinearProgress className={classes.appLoader} color="primary" />
|
||||
) : (
|
||||
<div className={classes.appLoaderPlaceholder} />
|
||||
)}
|
||||
<div
|
||||
className={clsx(classes.viewContainer, {
|
||||
[fullSizeClasses.viewContainer]: fullSize,
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<Container>
|
||||
<div className={classes.header}>
|
||||
<div className={classes.headerAnchor} ref={appHeaderAnchor} />
|
||||
<div className={classes.headerToolbar}>
|
||||
{!isMdUp && (
|
||||
<SidebarDrawer
|
||||
menuItems={menuStructure}
|
||||
logoHref="/"
|
||||
onMenuItemClick={handleMenuItemClick}
|
||||
linkComponent={SidebarLink}
|
||||
/>
|
||||
)}
|
||||
<div className={classes.spacer} />
|
||||
<div className={classes.userBar}>
|
||||
<NavigatorButton
|
||||
isMac={navigator.platform.toLowerCase().includes("mac")}
|
||||
onClick={() => setNavigatorVisibility(true)}
|
||||
/>
|
||||
{isPickerActive && (
|
||||
<AppChannelSelect
|
||||
channels={availableChannels}
|
||||
selectedChannelId={channel?.id}
|
||||
onChannelSelect={setChannel}
|
||||
/>
|
||||
)}
|
||||
<UserChip
|
||||
isDarkThemeEnabled={isDarkTheme(themeType)}
|
||||
user={user}
|
||||
onLogout={logout}
|
||||
onThemeToggle={toggleTheme}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
<main
|
||||
className={clsx(classes.view, {
|
||||
[classes.viewMargins]: !fullSize,
|
||||
[fullSizeClasses.view]: fullSize,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
<div className={classes.appAction} ref={appActionAnchor} />
|
||||
</div>
|
||||
</div>
|
||||
<Sidebar />
|
||||
</Box>
|
||||
<Box height="100%" width="100%">
|
||||
<Box as="main" width="100%">
|
||||
{children}
|
||||
</Box>
|
||||
<Box
|
||||
ref={appActionAnchor}
|
||||
position="sticky"
|
||||
bottom={0}
|
||||
left={0}
|
||||
right={0}
|
||||
backgroundColor="plain"
|
||||
borderTopWidth={1}
|
||||
borderTopStyle="solid"
|
||||
borderColor="neutralPlain"
|
||||
__maxWidth={contentMaxWidth}
|
||||
margin="auto"
|
||||
// @ts-ignore
|
||||
__zIndex="3"
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
21
src/components/AppLayout/Content.tsx
Normal file
21
src/components/AppLayout/Content.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
import { borderHeight, savebarHeight, topBarHeight } from "./consts";
|
||||
|
||||
interface ContentProps {
|
||||
[key: `data-${string}`]: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Content: React.FC<ContentProps> = ({ children, ...rest }) => (
|
||||
<Box
|
||||
__gridArea="content"
|
||||
__height={`calc(100vh - ${topBarHeight} - ${savebarHeight} - ${borderHeight})`}
|
||||
overflowY="auto"
|
||||
className="hide-scrollbar"
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
42
src/components/AppLayout/DetailedContent.tsx
Normal file
42
src/components/AppLayout/DetailedContent.tsx
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { Box, tabletMediaQuery } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { useMediaQuery } from "usehooks-ts";
|
||||
|
||||
import { contentMaxWidth } from "./consts";
|
||||
|
||||
interface DetailedContentProps {
|
||||
children: React.ReactNode;
|
||||
useSingleColumn?: boolean;
|
||||
constHeight?: boolean;
|
||||
}
|
||||
|
||||
const getLayoutAreas = (useSingleColumn: boolean, isTablet: boolean) => {
|
||||
if (useSingleColumn) {
|
||||
return '"nav" "content"';
|
||||
}
|
||||
|
||||
return isTablet ? '"nav right" "content right"' : '"nav" "content" "right"';
|
||||
};
|
||||
|
||||
export const DetailedContent: React.FC<DetailedContentProps> = ({
|
||||
children,
|
||||
useSingleColumn = false,
|
||||
constHeight = false,
|
||||
}) => {
|
||||
const isTablet = useMediaQuery(tabletMediaQuery);
|
||||
|
||||
return (
|
||||
<Box
|
||||
as="div"
|
||||
display="grid"
|
||||
height={constHeight ? "100vh" : "100%"}
|
||||
margin="auto"
|
||||
__maxWidth={contentMaxWidth}
|
||||
__gridTemplateColumns={useSingleColumn ? "1fr" : "9fr 4fr"}
|
||||
__gridTemplateRows="auto 1fr"
|
||||
__gridTemplateAreas={getLayoutAreas(useSingleColumn, isTablet)}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
12
src/components/AppLayout/LimitsInfo.tsx
Normal file
12
src/components/AppLayout/LimitsInfo.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
interface LimitsInfoProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const LimitsInfo: React.FC<LimitsInfoProps> = ({ text }) => (
|
||||
<Box position="absolute" left={10} bottom={3}>
|
||||
{text}
|
||||
</Box>
|
||||
);
|
33
src/components/AppLayout/RightSidebar.tsx
Normal file
33
src/components/AppLayout/RightSidebar.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
|
||||
import { borderHeight, savebarHeight } from "./consts";
|
||||
|
||||
interface RightSidebarProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const RightSidebar: React.FC<RightSidebarProps> = ({
|
||||
children,
|
||||
className,
|
||||
}) => (
|
||||
<Box
|
||||
borderStyle="solid"
|
||||
borderColor="neutralPlain"
|
||||
borderLeftWidth={1}
|
||||
__height={`calc(100vh - ${savebarHeight} - ${borderHeight})`}
|
||||
position="sticky"
|
||||
top={0}
|
||||
overflowY="auto"
|
||||
borderYWidth={0}
|
||||
borderTopWidth={0}
|
||||
borderBottomWidth={0}
|
||||
borderRightWidth={0}
|
||||
__gridArea="right"
|
||||
className={clsx("hide-scrollbar", className)}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
|
@ -1,13 +0,0 @@
|
|||
import React from "react";
|
||||
import { Link, LinkProps } from "react-router-dom";
|
||||
|
||||
interface SidebarLinkProps extends Omit<LinkProps, "to"> {
|
||||
href?: string;
|
||||
}
|
||||
|
||||
export const SidebarLink = React.forwardRef<
|
||||
HTMLAnchorElement,
|
||||
SidebarLinkProps
|
||||
>(({ href, ...props }, ref) => <Link to={href} {...props} innerRef={ref} />);
|
||||
|
||||
SidebarLink.displayName = "SidebarLink";
|
72
src/components/AppLayout/TopNav.tsx
Normal file
72
src/components/AppLayout/TopNav.tsx
Normal file
|
@ -0,0 +1,72 @@
|
|||
import { ArrowLeftIcon, Box, sprinkles, Text } from "@saleor/macaw-ui/next";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import useAppChannel from "./AppChannelContext";
|
||||
import AppChannelSelect from "./AppChannelSelect";
|
||||
import { topBarHeight } from "./consts";
|
||||
|
||||
interface TopNavProps {
|
||||
title: string | React.ReactNode;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
export const TopNav: React.FC<PropsWithChildren<TopNavProps>> = ({
|
||||
title,
|
||||
href,
|
||||
children,
|
||||
}) => {
|
||||
const { availableChannels, channel, isPickerActive, setChannel } =
|
||||
useAppChannel(false);
|
||||
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
paddingX={9}
|
||||
paddingRight={9}
|
||||
paddingY={8}
|
||||
borderBottomWidth={1}
|
||||
borderBottomStyle="solid"
|
||||
borderColor="neutralPlain"
|
||||
position="relative"
|
||||
__gridArea="nav"
|
||||
data-test-id="page-header"
|
||||
__height={topBarHeight}
|
||||
>
|
||||
{href && (
|
||||
<Link
|
||||
to={href}
|
||||
data-test-id="app-header-back-button"
|
||||
className={sprinkles({
|
||||
borderColor: "neutralPlain",
|
||||
borderStyle: "solid",
|
||||
borderWidth: 1,
|
||||
padding: 5,
|
||||
borderRadius: 2,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginRight: 7,
|
||||
})}
|
||||
>
|
||||
<ArrowLeftIcon />
|
||||
</Link>
|
||||
)}
|
||||
<Box __flex={1}>
|
||||
<Text variant="title">{title}</Text>
|
||||
</Box>
|
||||
<Box display="flex" flexWrap="nowrap">
|
||||
{isPickerActive && (
|
||||
<AppChannelSelect
|
||||
channels={availableChannels}
|
||||
selectedChannelId={channel?.id}
|
||||
onChannelSelect={setChannel}
|
||||
/>
|
||||
)}
|
||||
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
|
@ -1,6 +1,5 @@
|
|||
export const drawerWidthExpanded = 210;
|
||||
export const drawerWidthExpandedMobile = 250;
|
||||
export const drawerWidth = 80;
|
||||
export const drawerNestedMenuWidth = 300;
|
||||
export const navigationBarHeight = 64;
|
||||
export const appLoaderHeight = 4;
|
||||
export const topBarHeight = "77px";
|
||||
export const appLoaderHeight = 2;
|
||||
export const contentMaxWidth = "1440px";
|
||||
export const savebarHeight = "64px";
|
||||
export const borderHeight = "1px";
|
||||
|
|
|
@ -16,14 +16,10 @@ export const useStyles = makeStyles(
|
|||
},
|
||||
appLoader: {
|
||||
height: appLoaderHeight,
|
||||
marginBottom: theme.spacing(4),
|
||||
zIndex: 1201,
|
||||
position: "fixed",
|
||||
width: "100%",
|
||||
},
|
||||
appLoaderPlaceholder: {
|
||||
height: appLoaderHeight,
|
||||
marginBottom: theme.spacing(4),
|
||||
},
|
||||
|
||||
content: {
|
||||
flex: 1,
|
||||
[theme.breakpoints.up("md")]: {
|
||||
|
|
|
@ -123,7 +123,7 @@ const AssignAttributeDialog: React.FC<AssignAttributeDialogProps> = ({
|
|||
paper: classes.dialogPaper,
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<DialogTitle disableTypography>
|
||||
<FormattedMessage {...messages.title} />
|
||||
</DialogTitle>
|
||||
<DialogContent className={classes.searchArea}>
|
||||
|
|
|
@ -92,7 +92,7 @@ const AssignContainerDialog: React.FC<AssignContainerDialogProps> = props => {
|
|||
fullWidth
|
||||
maxWidth="sm"
|
||||
>
|
||||
<DialogTitle>{labels.title}</DialogTitle>
|
||||
<DialogTitle disableTypography>{labels.title}</DialogTitle>
|
||||
<DialogContent>
|
||||
<TextField
|
||||
name="query"
|
||||
|
|
|
@ -108,7 +108,7 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
|
|||
fullWidth
|
||||
maxWidth="sm"
|
||||
>
|
||||
<DialogTitle>
|
||||
<DialogTitle disableTypography>
|
||||
<FormattedMessage {...messages.assignVariantDialogHeader} />
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
|
|
|
@ -94,7 +94,7 @@ const AssignVariantDialog: React.FC<AssignVariantDialogProps> = props => {
|
|||
fullWidth
|
||||
maxWidth="sm"
|
||||
>
|
||||
<DialogTitle>
|
||||
<DialogTitle disableTypography>
|
||||
<FormattedMessage {...messages.assignVariantDialogHeader} />
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
|
|
|
@ -12,6 +12,7 @@ const useStyles = makeStyles(
|
|||
);
|
||||
|
||||
interface ControlledSwitchProps {
|
||||
className?: string;
|
||||
checked: boolean;
|
||||
disabled?: boolean;
|
||||
label: string | React.ReactNode;
|
||||
|
@ -30,12 +31,14 @@ export const ControlledSwitch: React.FC<ControlledSwitchProps> = props => {
|
|||
name,
|
||||
secondLabel,
|
||||
uncheckedLabel,
|
||||
className,
|
||||
} = props;
|
||||
|
||||
const classes = useStyles(props);
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
className={className}
|
||||
control={
|
||||
<Switch
|
||||
onChange={() =>
|
||||
|
|
|
@ -21,7 +21,11 @@ import ColumnPicker from "../ColumnPicker";
|
|||
import { FullScreenContainer } from "./FullScreenContainer";
|
||||
import { Header } from "./Header";
|
||||
import { RowActions } from "./RowActions";
|
||||
import useStyles, { useDatagridTheme, useFullScreenStyles } from "./styles";
|
||||
import useStyles, {
|
||||
cellHeight,
|
||||
useDatagridTheme,
|
||||
useFullScreenStyles,
|
||||
} from "./styles";
|
||||
import { AvailableColumn } from "./types";
|
||||
import useCells from "./useCells";
|
||||
import useColumns from "./useColumns";
|
||||
|
@ -108,9 +112,8 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
|||
|
||||
const [scrolledToRight, setScrolledToRight] = React.useState(false);
|
||||
const scroller: HTMLDivElement = document.querySelector(".dvn-scroller");
|
||||
const scrollerInner: HTMLDivElement = document.querySelector(
|
||||
".dvn-scroll-inner",
|
||||
);
|
||||
const scrollerInner: HTMLDivElement =
|
||||
document.querySelector(".dvn-scroll-inner");
|
||||
|
||||
usePreventHistoryBack(scroller);
|
||||
|
||||
|
@ -260,8 +263,8 @@ export const Datagrid: React.FC<DatagridProps> = ({
|
|||
onColumnResize={onColumnResize}
|
||||
onGridSelectionChange={setSelection}
|
||||
gridSelection={selection}
|
||||
rowHeight={48}
|
||||
headerHeight={48}
|
||||
rowHeight={cellHeight}
|
||||
headerHeight={cellHeight}
|
||||
ref={editor}
|
||||
onPaste
|
||||
rightElementProps={{
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { Theme } from "@glideapps/glide-data-grid";
|
||||
import { Typography } from "@material-ui/core/styles/createTypography";
|
||||
import { makeStyles, useTheme } from "@saleor/macaw-ui";
|
||||
import { themes } from "@saleor/macaw-ui/next";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export const cellHeight = 36;
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => {
|
||||
const rowActionSelected = {
|
||||
|
@ -19,10 +21,10 @@ const useStyles = makeStyles(
|
|||
background: theme.palette.background.paper,
|
||||
borderRadius: 8,
|
||||
// Right and left toolbars
|
||||
width: "calc(100% - 64px - 48px - 1px)",
|
||||
width: `calc(100% - 64px - ${cellHeight} - 1px)`,
|
||||
marginTop: 1,
|
||||
marginLeft: 50,
|
||||
height: 48,
|
||||
height: cellHeight,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-end",
|
||||
|
@ -32,16 +34,17 @@ const useStyles = makeStyles(
|
|||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: 48,
|
||||
height: cellHeight,
|
||||
},
|
||||
ghostIcon: {
|
||||
color: theme.palette.saleor.main[3],
|
||||
},
|
||||
portal: {
|
||||
"& input::-webkit-outer-spin-button, input::-webkit-inner-spin-button": {
|
||||
appearance: "none",
|
||||
margin: 0,
|
||||
},
|
||||
"& input::-webkit-outer-spin-button, input::-webkit-inner-spin-button":
|
||||
{
|
||||
appearance: "none",
|
||||
margin: 0,
|
||||
},
|
||||
"& input[type=number]": {
|
||||
appearance: "textfield",
|
||||
},
|
||||
|
@ -61,13 +64,14 @@ const useStyles = makeStyles(
|
|||
padding: "0 !important",
|
||||
},
|
||||
"& input, & textarea": {
|
||||
...theme.typography.body1,
|
||||
appearance: "none",
|
||||
background: "none",
|
||||
border: "none",
|
||||
fontSize: theme.typography.body1.fontSize,
|
||||
letterSpacing: "0.44px",
|
||||
padding: `1.4rem ${theme.spacing(1)}`,
|
||||
fontSize: themes.defaultLight.fontSize.bodySmall,
|
||||
letterSpacing: "0.015em",
|
||||
lineHeight: themes.defaultLight.lineHeight.bodySmall,
|
||||
fontWeight: themes.defaultLight.fontWeight.bodySmall,
|
||||
padding: themes.defaultLight.space[3],
|
||||
outline: 0,
|
||||
},
|
||||
'& input[type="number"]': {
|
||||
|
@ -98,7 +102,7 @@ const useStyles = makeStyles(
|
|||
height: "100%",
|
||||
background: theme.palette.background.paper,
|
||||
borderLeft: `1px solid ${activeBorderColor}`,
|
||||
width: 48,
|
||||
width: 36,
|
||||
},
|
||||
rowActionBarScrolledToRight: {
|
||||
borderLeftColor: theme.palette.divider,
|
||||
|
@ -119,7 +123,7 @@ const useStyles = makeStyles(
|
|||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: 47,
|
||||
height: `calc(${cellHeight}px - 1px)`,
|
||||
},
|
||||
rowActionScrolledToRight: {
|
||||
borderLeftColor: theme.palette.divider,
|
||||
|
@ -128,7 +132,7 @@ const useStyles = makeStyles(
|
|||
position: "absolute",
|
||||
top: 1,
|
||||
left: 0,
|
||||
height: 48,
|
||||
height: cellHeight,
|
||||
width: 10,
|
||||
borderLeft: 0,
|
||||
background: theme.palette.background.paper,
|
||||
|
@ -176,21 +180,9 @@ export const useFullScreenStyles = makeStyles<ReturnType<typeof useStyles>>(
|
|||
{ name: "Datagrid-fullscreen" },
|
||||
);
|
||||
|
||||
const calculateFontToPx = (remValue: string | number, base: number) => {
|
||||
if (typeof remValue === "string") {
|
||||
return `${parseFloat(remValue) * base}px`;
|
||||
}
|
||||
|
||||
return `${remValue * base}px`;
|
||||
};
|
||||
|
||||
type HtmlTypography = Typography & { htmlFontSize: number };
|
||||
|
||||
export function useDatagridTheme() {
|
||||
const theme = useTheme();
|
||||
|
||||
const base = (theme.typography as HtmlTypography).htmlFontSize * 0.625;
|
||||
|
||||
const datagridTheme = useMemo(
|
||||
(): Partial<Theme> => ({
|
||||
accentColor: theme.palette.secondary.main,
|
||||
|
@ -203,15 +195,18 @@ export function useDatagridTheme() {
|
|||
bgBubbleSelected: theme.palette.background.paper,
|
||||
textHeader: theme.palette.text.secondary,
|
||||
borderColor: theme.palette.divider,
|
||||
fontFamily: theme.typography.fontFamily,
|
||||
baseFontStyle: calculateFontToPx(theme.typography.body1.fontSize, base),
|
||||
headerFontStyle: calculateFontToPx(theme.typography.body2.fontSize, base),
|
||||
editorFontSize: calculateFontToPx(theme.typography.body1.fontSize, base),
|
||||
fontFamily: themes.defaultLight.fontFamily.body,
|
||||
baseFontStyle: themes.defaultLight.fontSize.bodySmall,
|
||||
headerFontStyle: themes.defaultLight.fontSize.bodySmall,
|
||||
editorFontSize: themes.defaultLight.fontSize.bodySmall,
|
||||
textMedium: theme.palette.text.primary,
|
||||
textGroupHeader: theme.palette.text.secondary,
|
||||
textBubble: theme.palette.text.primary,
|
||||
textDark: theme.palette.text.primary,
|
||||
textLight: theme.palette.text.primary,
|
||||
cellHorizontalPadding: 8,
|
||||
cellVerticalPadding: 8,
|
||||
lineHeight: 20,
|
||||
}),
|
||||
[theme],
|
||||
);
|
||||
|
|
|
@ -42,7 +42,7 @@ const useStyles = makeStyles(
|
|||
root: {
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
height: "calc(100vh - 180px)",
|
||||
height: "100vh",
|
||||
},
|
||||
header: {
|
||||
marginBottom: theme.spacing(2),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { ClickAwayListener, Grow, Popper, Typography } from "@material-ui/core";
|
||||
import { alpha } from "@material-ui/core/styles";
|
||||
import { Button, makeStyles } from "@saleor/macaw-ui";
|
||||
import { vars } from "@saleor/macaw-ui/next";
|
||||
import clsx from "clsx";
|
||||
import React, { useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
@ -66,6 +67,10 @@ const useStyles = makeStyles(
|
|||
width: 240,
|
||||
},
|
||||
popover: {
|
||||
backgroundColor: vars.colors.background.surfaceNeutralPlain,
|
||||
overflowY: "scroll",
|
||||
boxShadow: `0px 6px 11px 9px ${theme.palette.divider}`,
|
||||
height: 450,
|
||||
width: 376,
|
||||
zIndex: 3,
|
||||
},
|
||||
|
@ -182,14 +187,8 @@ const Filter: React.FC<FilterProps> = props => {
|
|||
},
|
||||
}}
|
||||
>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Grow
|
||||
{...TransitionProps}
|
||||
style={{
|
||||
transformOrigin:
|
||||
placement === "bottom" ? "right top" : "right bottom",
|
||||
}}
|
||||
>
|
||||
{() => (
|
||||
<Grow>
|
||||
<FilterContent
|
||||
errorMessages={errorMessages}
|
||||
errors={filterErrors}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import CollectionWithDividers from "@dashboard/components/CollectionWithDividers";
|
||||
import Hr from "@dashboard/components/Hr";
|
||||
import useStateFromProps from "@dashboard/hooks/useStateFromProps";
|
||||
import { makeStyles, Paper, Typography } from "@material-ui/core";
|
||||
import { Accordion, AccordionSummary } from "@saleor/macaw-ui";
|
||||
|
@ -121,12 +120,10 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
|||
{},
|
||||
);
|
||||
|
||||
const [
|
||||
autocompleteDisplayValues,
|
||||
setAutocompleteDisplayValues,
|
||||
] = useStateFromProps<FilterAutocompleteDisplayValues>(
|
||||
initialAutocompleteDisplayValues,
|
||||
);
|
||||
const [autocompleteDisplayValues, setAutocompleteDisplayValues] =
|
||||
useStateFromProps<FilterAutocompleteDisplayValues>(
|
||||
initialAutocompleteDisplayValues,
|
||||
);
|
||||
|
||||
const commonFilterBodyProps: Omit<
|
||||
FilterContentBodyProps<string>,
|
||||
|
@ -153,9 +150,9 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
const handleFilterPropertyGroupChange = function<
|
||||
const handleFilterPropertyGroupChange = function <
|
||||
K extends string,
|
||||
T extends FieldType
|
||||
T extends FieldType,
|
||||
>(action: FilterReducerAction<K, T>, filter: FilterElement<string>) {
|
||||
const switchToActive = action.payload.update.active;
|
||||
if (switchToActive && filter.name !== openedFilter?.name) {
|
||||
|
@ -169,9 +166,9 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
|||
onFilterPropertyChange(action);
|
||||
};
|
||||
|
||||
const handleMultipleFieldPropertyChange = function<
|
||||
const handleMultipleFieldPropertyChange = function <
|
||||
K extends string,
|
||||
T extends FieldType
|
||||
T extends FieldType,
|
||||
>(action: FilterReducerAction<K, T>) {
|
||||
const { update } = action.payload;
|
||||
onFilterPropertyChange({
|
||||
|
@ -180,7 +177,7 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
|||
});
|
||||
};
|
||||
|
||||
const getFilterFromCurrentData = function<T extends string>(
|
||||
const getFilterFromCurrentData = function <T extends string>(
|
||||
filter: FilterElement<T>,
|
||||
) {
|
||||
return filters.find(({ name }) => filter.name === name);
|
||||
|
@ -195,7 +192,7 @@ const FilterContent: React.FC<FilterContentProps> = ({
|
|||
}}
|
||||
>
|
||||
<FilterContentHeader onClear={onClear} />
|
||||
<Hr />
|
||||
|
||||
{dataStructure
|
||||
.sort((a, b) => (a.name > b.name ? 1 : -1))
|
||||
.map(filter => {
|
||||
|
|
|
@ -5,7 +5,6 @@ import { useCommonStyles } from "@dashboard/components/Filter/FilterContent/util
|
|||
import { MultiAutocompleteChoiceType } from "@dashboard/components/MultiAutocompleteSelectField";
|
||||
import Skeleton from "@dashboard/components/Skeleton";
|
||||
import { FormControlLabel, Radio, TextField } from "@material-ui/core";
|
||||
import { alpha } from "@material-ui/core/styles";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
|
@ -27,7 +26,6 @@ import {
|
|||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
filterSettings: {
|
||||
background: alpha(theme.palette.primary.main, 0.1),
|
||||
padding: theme.spacing(2, 3),
|
||||
},
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Button } from "@dashboard/components/Button";
|
|||
import { buttonMessages } from "@dashboard/intl";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { vars } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
|
@ -11,7 +12,12 @@ const useStyles = makeStyles(
|
|||
alignItems: "center",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
padding: theme.spacing(1, 3),
|
||||
backgroundColor: vars.colors.background.surfaceNeutralPlain,
|
||||
borderBottom: `1px solid ${vars.colors.border.neutralPlain}`,
|
||||
zIndex: 1,
|
||||
},
|
||||
clear: {
|
||||
marginRight: theme.spacing(1),
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { alpha } from "@material-ui/core";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
|
@ -10,7 +9,6 @@ const useStyles = makeStyles(
|
|||
marginRight: theme.spacing(2),
|
||||
},
|
||||
filterSettings: {
|
||||
background: alpha(theme.palette.primary.main, 0.1),
|
||||
padding: theme.spacing(2, 3),
|
||||
},
|
||||
inputRange: {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { vars } from "@saleor/macaw-ui/next";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
|
||||
|
@ -7,16 +8,16 @@ interface HrProps {
|
|||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
{
|
||||
root: {
|
||||
backgroundColor: theme.palette.divider,
|
||||
backgroundColor: vars.colors.border.neutralPlain,
|
||||
border: "none",
|
||||
display: "block",
|
||||
height: 1,
|
||||
margin: 0,
|
||||
width: "100%",
|
||||
},
|
||||
}),
|
||||
},
|
||||
{ name: "Hr" },
|
||||
);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Typography } from "@material-ui/core";
|
||||
import { alpha } from "@material-ui/core/styles";
|
||||
import { ImageIcon, makeStyles } from "@saleor/macaw-ui";
|
||||
import { vars } from "@saleor/macaw-ui/next";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
@ -29,7 +30,7 @@ const useStyles = makeStyles(
|
|||
},
|
||||
imageContainer: {
|
||||
background: "#ffffff",
|
||||
border: "1px solid #eaeaea",
|
||||
border: `1px solid ${vars.colors.border.neutralPlain}`,
|
||||
borderRadius: theme.spacing(),
|
||||
height: 148,
|
||||
justifySelf: "start",
|
||||
|
|
|
@ -35,6 +35,7 @@ const useStyles = makeStyles(
|
|||
interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
||||
href?: string;
|
||||
color?: "primary" | "secondary";
|
||||
inline?: boolean;
|
||||
underline?: boolean;
|
||||
typographyProps?: TypographyProps;
|
||||
onClick?: React.MouseEventHandler;
|
||||
|
@ -45,6 +46,7 @@ const Link: React.FC<LinkProps> = props => {
|
|||
const {
|
||||
className,
|
||||
children,
|
||||
inline = true,
|
||||
color = "primary",
|
||||
underline = false,
|
||||
onClick,
|
||||
|
@ -60,13 +62,16 @@ const Link: React.FC<LinkProps> = props => {
|
|||
const opensNewTab = target === "_blank";
|
||||
|
||||
const commonLinkProps = {
|
||||
className: clsx(className, {
|
||||
[classes.root]: true,
|
||||
[classes[color]]: true,
|
||||
[classes.underline]: underline,
|
||||
[classes.noUnderline]: !underline,
|
||||
[classes.disabled]: disabled,
|
||||
}),
|
||||
className: clsx(
|
||||
{
|
||||
[classes.root]: inline,
|
||||
[classes[color]]: true,
|
||||
[classes.underline]: underline,
|
||||
[classes.noUnderline]: !underline,
|
||||
[classes.disabled]: disabled,
|
||||
},
|
||||
className,
|
||||
),
|
||||
onClick: event => {
|
||||
if (disabled || !onClick) {
|
||||
return;
|
||||
|
|
|
@ -31,6 +31,7 @@ const useStyles = makeStyles(
|
|||
"&&": {
|
||||
paddingBottom: theme.spacing(2),
|
||||
paddingTop: theme.spacing(2),
|
||||
paddingLeft: theme.spacing(4),
|
||||
},
|
||||
},
|
||||
content: {
|
||||
|
|
|
@ -43,7 +43,7 @@ const useStyles = makeStyles(
|
|||
root: {
|
||||
alignItems: "center",
|
||||
display: "flex",
|
||||
height: "calc(100vh - 180px)",
|
||||
height: "100vh",
|
||||
},
|
||||
}),
|
||||
{ name: "NotFoundPage" },
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import VerticalSpacer from "@dashboard/apps/components/VerticalSpacer";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
interface PageSectionHeaderProps {
|
||||
|
@ -11,11 +12,11 @@ const PageSectionHeader: React.FC<PageSectionHeaderProps> = props => {
|
|||
const { title, description } = props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Box paddingTop={9}>
|
||||
{title && <Typography variant="h5">{title}</Typography>}
|
||||
{title && description && <VerticalSpacer />}
|
||||
{description && <Typography variant="body2">{description}</Typography>}
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -99,11 +99,6 @@ const useStyles = makeStyles(
|
|||
borderColor: isDarkMode
|
||||
? theme.palette.saleor.main[2]
|
||||
: theme.palette.saleor.main[4],
|
||||
boxShadow: `0 0 0 3px ${
|
||||
isDarkMode
|
||||
? theme.palette.saleor.main[4]
|
||||
: theme.palette.saleor.main[6]
|
||||
}`,
|
||||
},
|
||||
},
|
||||
root: {
|
||||
|
|
|
@ -48,7 +48,7 @@ const SaveFilterTabDialog: React.FC<SaveFilterTabDialogProps> = ({
|
|||
|
||||
return (
|
||||
<Dialog onClose={onClose} open={open} fullWidth maxWidth="sm">
|
||||
<DialogTitle>
|
||||
<DialogTitle disableTypography>
|
||||
<FormattedMessage
|
||||
id="liLrVs"
|
||||
defaultMessage="Save Custom Search"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { buttonMessages, commonMessages } from "@dashboard/intl";
|
||||
import {
|
||||
makeStyles,
|
||||
Savebar as MacawSavebar,
|
||||
SavebarLabels,
|
||||
SavebarProps as MacawSavebarProps,
|
||||
|
@ -7,12 +8,38 @@ import {
|
|||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { contentMaxWidth, savebarHeight } from "./AppLayout/consts";
|
||||
|
||||
export interface SavebarProps extends Omit<MacawSavebarProps, "labels"> {
|
||||
labels?: Partial<SavebarLabels>;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
root: {
|
||||
height: savebarHeight,
|
||||
"& .MuiContainer-root": {
|
||||
paddingRight: 0,
|
||||
paddingLeft: 0,
|
||||
maxWidth: contentMaxWidth,
|
||||
margin: "0 auto",
|
||||
},
|
||||
"& .MuiPaper-root": {
|
||||
boxShadow: "none",
|
||||
},
|
||||
"& .MuiCardContent-root": {
|
||||
marginTop: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Savebar",
|
||||
},
|
||||
);
|
||||
|
||||
export const Savebar: React.FC<SavebarProps> = ({ labels = {}, ...rest }) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
|
||||
const defaultLabels: SavebarLabels = {
|
||||
cancel: intl.formatMessage(buttonMessages.back),
|
||||
|
@ -25,7 +52,9 @@ export const Savebar: React.FC<SavebarProps> = ({ labels = {}, ...rest }) => {
|
|||
...labels,
|
||||
};
|
||||
|
||||
return <MacawSavebar labels={componentLabels} {...rest} />;
|
||||
return (
|
||||
<MacawSavebar labels={componentLabels} {...rest} className={classes.root} />
|
||||
);
|
||||
};
|
||||
Savebar.displayName = "SaveBar";
|
||||
export default Savebar;
|
||||
|
|
20
src/components/Sidebar/Content.tsx
Normal file
20
src/components/Sidebar/Content.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
import { Menu } from "./menu";
|
||||
import { MountingPoint } from "./MountingPoint";
|
||||
import { UserInfo } from "./user";
|
||||
|
||||
export const SidebarContent = () => (
|
||||
<Box
|
||||
backgroundColor="subdued"
|
||||
as="aside"
|
||||
height="100%"
|
||||
display="grid"
|
||||
__gridTemplateRows="auto 1fr auto"
|
||||
>
|
||||
<MountingPoint />
|
||||
<Menu />
|
||||
<UserInfo />
|
||||
</Box>
|
||||
);
|
12
src/components/Sidebar/MountingPoint.tsx
Normal file
12
src/components/Sidebar/MountingPoint.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
import sideBarDefaultLogo from "@assets/images/sidebar-default-logo.png";
|
||||
import { Avatar, Box, Text } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
export const MountingPoint = () => (
|
||||
<Box display="flex" gap={6} paddingX={7} paddingY={8}>
|
||||
<Avatar.Store src={sideBarDefaultLogo} scheme="decorative2" size="small" />
|
||||
<Text variant="bodyStrong" size="small">
|
||||
Saleor Dashboard
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
37
src/components/Sidebar/Sidebar.tsx
Normal file
37
src/components/Sidebar/Sidebar.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { Box, Drawer, MenuIcon } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
import { SidebarContent } from "./Content";
|
||||
|
||||
export const Sidebar = () => (
|
||||
<>
|
||||
<Box
|
||||
display={{ mobile: "none", tablet: "none", desktop: "block" }}
|
||||
height="100%"
|
||||
>
|
||||
<SidebarContent />
|
||||
</Box>
|
||||
<Box display={{ mobile: "block", tablet: "block", desktop: "none" }}>
|
||||
<Drawer>
|
||||
<Drawer.Trigger>
|
||||
<Box
|
||||
as="button"
|
||||
borderWidth={0}
|
||||
backgroundColor="interactiveNeutralHighlightDefault"
|
||||
cursor="pointer"
|
||||
data-test-id="sidebar-drawer-open"
|
||||
>
|
||||
<MenuIcon />
|
||||
</Box>
|
||||
</Drawer.Trigger>
|
||||
<Drawer.Content
|
||||
backgroundColor="subdued"
|
||||
data-test-id="sidebar-drawer-content"
|
||||
paddingTop={0}
|
||||
>
|
||||
<SidebarContent />
|
||||
</Drawer.Content>
|
||||
</Drawer>
|
||||
</Box>
|
||||
</>
|
||||
);
|
1
src/components/Sidebar/index.ts
Normal file
1
src/components/Sidebar/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from "./Sidebar";
|
16
src/components/Sidebar/menu/Divider.tsx
Normal file
16
src/components/Sidebar/menu/Divider.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { List, Text } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
import { SidebarMenuItem } from "./types";
|
||||
|
||||
interface Props {
|
||||
menuItem: SidebarMenuItem;
|
||||
}
|
||||
|
||||
export const Divider: React.FC<Props> = ({ menuItem }) => (
|
||||
<List.Divider paddingY={menuItem.paddingY ?? 4} paddingX={3}>
|
||||
<Text variant="caption" size="small" color="textNeutralSubdued">
|
||||
{menuItem.label}
|
||||
</Text>
|
||||
</List.Divider>
|
||||
);
|
22
src/components/Sidebar/menu/Item.tsx
Normal file
22
src/components/Sidebar/menu/Item.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from "react";
|
||||
|
||||
import { Divider } from "./Divider";
|
||||
import { ItemGroup } from "./ItemGroup";
|
||||
import { SingleItem } from "./SingleItem";
|
||||
import { SidebarMenuItem } from "./types";
|
||||
|
||||
interface Props {
|
||||
menuItem: SidebarMenuItem;
|
||||
}
|
||||
|
||||
export const MenuItem: React.FC<Props> = props => {
|
||||
const { menuItem } = props;
|
||||
switch (menuItem.type) {
|
||||
case "item":
|
||||
return <SingleItem {...props} />;
|
||||
case "itemGroup":
|
||||
return <ItemGroup {...props} />;
|
||||
case "divider":
|
||||
return <Divider menuItem={menuItem} />;
|
||||
}
|
||||
};
|
47
src/components/Sidebar/menu/ItemGroup.tsx
Normal file
47
src/components/Sidebar/menu/ItemGroup.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { Box, List, Text } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
import { MenuItem } from "./Item";
|
||||
import { SidebarMenuItem } from "./types";
|
||||
|
||||
interface Props {
|
||||
menuItem: SidebarMenuItem;
|
||||
}
|
||||
|
||||
export const ItemGroup: React.FC<Props> = ({ menuItem }) => (
|
||||
<List.ItemGroup>
|
||||
<List.ItemGroup.Trigger
|
||||
paddingX={5}
|
||||
paddingY={4}
|
||||
borderRadius={3}
|
||||
size="small"
|
||||
justifyContent="space-between"
|
||||
data-test-id={`menu-item-label-${menuItem.id}`}
|
||||
>
|
||||
<Box display="flex" alignItems="center" gap={6}>
|
||||
{menuItem.icon}
|
||||
<Text size="small" variant="bodyEmp">
|
||||
{menuItem.label}
|
||||
</Text>
|
||||
</Box>
|
||||
</List.ItemGroup.Trigger>
|
||||
<List.ItemGroup.Content>
|
||||
<Box
|
||||
borderLeftWidth={1}
|
||||
borderLeftStyle="solid"
|
||||
borderColor="neutralPlain"
|
||||
paddingLeft={7}
|
||||
marginLeft={7}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
marginBottom={5}
|
||||
marginTop={3}
|
||||
gap={1}
|
||||
>
|
||||
{menuItem.children?.map(child => (
|
||||
<MenuItem menuItem={child} key={child.id} />
|
||||
))}
|
||||
</Box>
|
||||
</List.ItemGroup.Content>
|
||||
</List.ItemGroup>
|
||||
);
|
19
src/components/Sidebar/menu/Menu.tsx
Normal file
19
src/components/Sidebar/menu/Menu.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Box, List } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
import { MenuItem } from "./Item";
|
||||
import { useMenuStructure } from "./useMenuStructure";
|
||||
|
||||
export const Menu = () => {
|
||||
const menuStructure = useMenuStructure();
|
||||
|
||||
return (
|
||||
<Box padding={6} overflowY="auto" className="hide-scrollbar">
|
||||
<List as="ol" display="grid" gap={3}>
|
||||
{menuStructure.map(menuItem => (
|
||||
<MenuItem menuItem={menuItem} key={menuItem.id} />
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
);
|
||||
};
|
52
src/components/Sidebar/menu/SingleItem.tsx
Normal file
52
src/components/Sidebar/menu/SingleItem.tsx
Normal file
|
@ -0,0 +1,52 @@
|
|||
import {
|
||||
extensionMountPoints,
|
||||
useExtensions,
|
||||
} from "@dashboard/apps/useExtensions";
|
||||
import { Box, List, sprinkles, Text } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { SidebarMenuItem } from "./types";
|
||||
import { getMenuItemExtension, isMenuActive } from "./utils";
|
||||
|
||||
interface Props {
|
||||
menuItem: SidebarMenuItem;
|
||||
}
|
||||
|
||||
export const SingleItem: React.FC<Props> = ({ menuItem }) => {
|
||||
const extensions = useExtensions(extensionMountPoints.NAVIGATION_SIDEBAR);
|
||||
const active = isMenuActive(location.pathname, menuItem);
|
||||
|
||||
const handleMenuItemClick = () => {
|
||||
const extension = getMenuItemExtension(extensions, menuItem.id);
|
||||
if (extension) {
|
||||
extension.open();
|
||||
return;
|
||||
}
|
||||
};
|
||||
return (
|
||||
<List.Item
|
||||
borderRadius={3}
|
||||
active={active}
|
||||
onClick={handleMenuItemClick}
|
||||
data-test-id={`menu-item-label-${menuItem.id}`}
|
||||
>
|
||||
<Link
|
||||
to={menuItem.url}
|
||||
className={sprinkles({
|
||||
paddingY: 4,
|
||||
paddingX: 5,
|
||||
display: "block",
|
||||
width: "100%",
|
||||
})}
|
||||
>
|
||||
<Box display="flex" alignItems="center" gap={6}>
|
||||
{menuItem.icon}
|
||||
<Text size="small" variant="bodyEmp">
|
||||
{menuItem.label}
|
||||
</Text>
|
||||
</Box>
|
||||
</Link>
|
||||
</List.Item>
|
||||
);
|
||||
};
|
1
src/components/Sidebar/menu/index.ts
Normal file
1
src/components/Sidebar/menu/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from "./Menu";
|
14
src/components/Sidebar/menu/types.ts
Normal file
14
src/components/Sidebar/menu/types.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { PermissionEnum } from "@dashboard/graphql";
|
||||
import { Sprinkles } from "@saleor/macaw-ui/next";
|
||||
|
||||
export interface SidebarMenuItem {
|
||||
label?: string;
|
||||
id: string;
|
||||
url?: string;
|
||||
permissions?: PermissionEnum[];
|
||||
type: "item" | "itemGroup" | "divider";
|
||||
icon?: React.ReactNode;
|
||||
onClick?: () => void;
|
||||
children?: SidebarMenuItem[];
|
||||
paddingY?: Sprinkles["paddingY"];
|
||||
}
|
|
@ -1,284 +1,294 @@
|
|||
import appsIcon from "@assets/images/menu-apps-icon.svg";
|
||||
import catalogIcon from "@assets/images/menu-catalog-icon.svg";
|
||||
import configurationIcon from "@assets/images/menu-configure-icon.svg";
|
||||
import customerIcon from "@assets/images/menu-customers-icon.svg";
|
||||
import discountsIcon from "@assets/images/menu-discounts-icon.svg";
|
||||
import homeIcon from "@assets/images/menu-home-icon.svg";
|
||||
import ordersIcon from "@assets/images/menu-orders-icon.svg";
|
||||
import pagesIcon from "@assets/images/menu-pages-icon.svg";
|
||||
import translationIcon from "@assets/images/menu-translation-icon.svg";
|
||||
import { appsListPath } from "@dashboard/apps/urls";
|
||||
import {
|
||||
extensionMountPoints,
|
||||
useExtensions,
|
||||
} from "@dashboard/apps/useExtensions";
|
||||
import { useUser } from "@dashboard/auth";
|
||||
import { categoryListUrl } from "@dashboard/categories/urls";
|
||||
import { collectionListUrl } from "@dashboard/collections/urls";
|
||||
import { MARKETPLACE_URL } from "@dashboard/config";
|
||||
import { configurationMenuUrl } from "@dashboard/configuration";
|
||||
import { getConfigMenuItemsPermissions } from "@dashboard/configuration/utils";
|
||||
import { customerListUrl } from "@dashboard/customers/urls";
|
||||
import { saleListUrl, voucherListUrl } from "@dashboard/discounts/urls";
|
||||
import { giftCardListUrl } from "@dashboard/giftCards/urls";
|
||||
import { PermissionEnum, UserFragment } from "@dashboard/graphql";
|
||||
import { PermissionEnum } from "@dashboard/graphql";
|
||||
import { commonMessages, sectionNames } from "@dashboard/intl";
|
||||
import { marketplaceUrlResolver } from "@dashboard/marketplace/marketplace-url-resolver";
|
||||
import { orderDraftListUrl, orderListUrl } from "@dashboard/orders/urls";
|
||||
import { pageListPath } from "@dashboard/pages/urls";
|
||||
import { SidebarMenuItem } from "@saleor/macaw-ui";
|
||||
import { IntlShape } from "react-intl";
|
||||
import { productListUrl } from "@dashboard/products/urls";
|
||||
import { languageListUrl } from "@dashboard/translations/urls";
|
||||
import {
|
||||
ConfigurationIcon,
|
||||
CustomersIcon,
|
||||
HomeIcon,
|
||||
MarketplaceIcon,
|
||||
OrdersIcon,
|
||||
ProductsIcons,
|
||||
StorefrontIcon,
|
||||
TranslationsIcon,
|
||||
VouchersIcon,
|
||||
} from "@saleor/macaw-ui/next";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import { appsListPath } from "../../apps/urls";
|
||||
import { categoryListUrl } from "../../categories/urls";
|
||||
import { collectionListUrl } from "../../collections/urls";
|
||||
import { customerListUrl } from "../../customers/urls";
|
||||
import { saleListUrl, voucherListUrl } from "../../discounts/urls";
|
||||
import { orderDraftListUrl, orderListUrl } from "../../orders/urls";
|
||||
import { productListUrl } from "../../products/urls";
|
||||
import { languageListUrl } from "../../translations/urls";
|
||||
import { getMenuItemExtension, mapToExtensionsItems } from "./utils";
|
||||
import { SidebarMenuItem } from "./types";
|
||||
import { mapToExtensionsItems } from "./utils";
|
||||
|
||||
export interface FilterableMenuItem extends Omit<SidebarMenuItem, "children"> {
|
||||
children?: FilterableMenuItem[];
|
||||
permissions?: PermissionEnum[];
|
||||
}
|
||||
const iconSettings = {
|
||||
color: "iconNeutralSubdued",
|
||||
size: "medium",
|
||||
} as const;
|
||||
|
||||
function useMenuStructure(
|
||||
intl: IntlShape,
|
||||
user: UserFragment,
|
||||
): [SidebarMenuItem[], (menuItem: SidebarMenuItem) => void] {
|
||||
export function useMenuStructure() {
|
||||
const extensions = useExtensions(extensionMountPoints.NAVIGATION_SIDEBAR);
|
||||
const intl = useIntl();
|
||||
const { user } = useUser();
|
||||
|
||||
const handleMenuItemClick = (menuItem: SidebarMenuItem) => {
|
||||
const extension = getMenuItemExtension(extensions, menuItem);
|
||||
if (extension) {
|
||||
extension.open();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const appExtensionsHeaderItem = {
|
||||
const appExtensionsHeaderItem: SidebarMenuItem = {
|
||||
id: "extensions",
|
||||
ariaLabel: "apps",
|
||||
label: intl.formatMessage(sectionNames.appExtensions),
|
||||
type: "divider",
|
||||
paddingY: 4,
|
||||
};
|
||||
|
||||
// This will be deleted when Marketplace is released
|
||||
// Consider this solution as temporary
|
||||
const getAppSection = () => {
|
||||
const getAppSection = (): SidebarMenuItem => {
|
||||
if (MARKETPLACE_URL) {
|
||||
return {
|
||||
ariaLabel: "apps_section",
|
||||
iconSrc: appsIcon,
|
||||
icon: <MarketplaceIcon {...iconSettings} />,
|
||||
label: intl.formatMessage(sectionNames.apps),
|
||||
permissions: [PermissionEnum.MANAGE_APPS],
|
||||
id: "apps_section",
|
||||
type: "itemGroup",
|
||||
children: [
|
||||
{
|
||||
label: intl.formatMessage(sectionNames.apps),
|
||||
id: "apps",
|
||||
url: appsListPath,
|
||||
type: "item",
|
||||
},
|
||||
{
|
||||
ariaLabel: "marketplace",
|
||||
label: intl.formatMessage(sectionNames.marketplace),
|
||||
id: "marketplace-saleor-apps",
|
||||
url: marketplaceUrlResolver.getSaleorAppsDashboardPath(),
|
||||
type: "item",
|
||||
},
|
||||
{
|
||||
ariaLabel: "marketplace",
|
||||
label: intl.formatMessage(sectionNames.appTemplateGallery),
|
||||
id: "marketplace-template-gallery",
|
||||
url: marketplaceUrlResolver.getTemplateGalleryDashboardPath(),
|
||||
type: "item",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
ariaLabel: "apps",
|
||||
iconSrc: appsIcon,
|
||||
icon: <MarketplaceIcon {...iconSettings} />,
|
||||
label: intl.formatMessage(sectionNames.apps),
|
||||
permissions: [PermissionEnum.MANAGE_APPS],
|
||||
id: "apps",
|
||||
url: appsListPath,
|
||||
type: "item",
|
||||
};
|
||||
};
|
||||
|
||||
const menuItems: FilterableMenuItem[] = [
|
||||
const menuItems: SidebarMenuItem[] = [
|
||||
{
|
||||
ariaLabel: "home",
|
||||
iconSrc: homeIcon,
|
||||
icon: <HomeIcon {...iconSettings} />,
|
||||
label: intl.formatMessage(sectionNames.home),
|
||||
id: "home",
|
||||
url: "/",
|
||||
type: "item",
|
||||
},
|
||||
{
|
||||
ariaLabel: "catalogue",
|
||||
children: [
|
||||
{
|
||||
ariaLabel: "products",
|
||||
label: intl.formatMessage(sectionNames.products),
|
||||
id: "products",
|
||||
url: productListUrl(),
|
||||
permissions: [PermissionEnum.MANAGE_PRODUCTS],
|
||||
type: "item",
|
||||
},
|
||||
{
|
||||
ariaLabel: "categories",
|
||||
label: intl.formatMessage(sectionNames.categories),
|
||||
id: "categories",
|
||||
url: categoryListUrl(),
|
||||
permissions: [PermissionEnum.MANAGE_PRODUCTS],
|
||||
type: "item",
|
||||
},
|
||||
{
|
||||
ariaLabel: "collections",
|
||||
label: intl.formatMessage(sectionNames.collections),
|
||||
id: "collections",
|
||||
url: collectionListUrl(),
|
||||
permissions: [PermissionEnum.MANAGE_PRODUCTS],
|
||||
type: "item",
|
||||
},
|
||||
{
|
||||
ariaLabel: "giftCards",
|
||||
label: intl.formatMessage(sectionNames.giftCards),
|
||||
id: "giftCards",
|
||||
url: giftCardListUrl(),
|
||||
permissions: [PermissionEnum.MANAGE_GIFT_CARD],
|
||||
type: "item",
|
||||
},
|
||||
...mapToExtensionsItems(
|
||||
extensions.NAVIGATION_CATALOG,
|
||||
appExtensionsHeaderItem,
|
||||
),
|
||||
],
|
||||
iconSrc: catalogIcon,
|
||||
label: intl.formatMessage(commonMessages.catalog),
|
||||
icon: <ProductsIcons {...iconSettings} />,
|
||||
label: intl.formatMessage(commonMessages.products),
|
||||
permissions: [
|
||||
PermissionEnum.MANAGE_GIFT_CARD,
|
||||
PermissionEnum.MANAGE_PRODUCTS,
|
||||
],
|
||||
id: "catalogue",
|
||||
id: "products",
|
||||
type: "itemGroup",
|
||||
},
|
||||
{
|
||||
id: "divider-1",
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
ariaLabel: "orders",
|
||||
children: [
|
||||
{
|
||||
ariaLabel: "orders",
|
||||
label: intl.formatMessage(sectionNames.orders),
|
||||
permissions: [PermissionEnum.MANAGE_ORDERS],
|
||||
id: "orders",
|
||||
url: orderListUrl(),
|
||||
type: "item",
|
||||
},
|
||||
{
|
||||
ariaLabel: "order drafts",
|
||||
label: intl.formatMessage(commonMessages.drafts),
|
||||
permissions: [PermissionEnum.MANAGE_ORDERS],
|
||||
id: "order-drafts",
|
||||
url: orderDraftListUrl(),
|
||||
type: "item",
|
||||
},
|
||||
...mapToExtensionsItems(
|
||||
extensions.NAVIGATION_ORDERS,
|
||||
appExtensionsHeaderItem,
|
||||
),
|
||||
],
|
||||
iconSrc: ordersIcon,
|
||||
icon: <OrdersIcon {...iconSettings} />,
|
||||
label: intl.formatMessage(sectionNames.orders),
|
||||
permissions: [PermissionEnum.MANAGE_ORDERS],
|
||||
id: "orders",
|
||||
type: "itemGroup",
|
||||
},
|
||||
{
|
||||
ariaLabel: "customers",
|
||||
children: extensions.NAVIGATION_CUSTOMERS.length > 0 && [
|
||||
children: !isEmpty(extensions.NAVIGATION_CUSTOMERS) && [
|
||||
{
|
||||
ariaLabel: "customers",
|
||||
label: intl.formatMessage(sectionNames.customers),
|
||||
permissions: [PermissionEnum.MANAGE_USERS],
|
||||
id: "customers",
|
||||
url: customerListUrl(),
|
||||
type: "item",
|
||||
},
|
||||
...mapToExtensionsItems(
|
||||
extensions.NAVIGATION_CUSTOMERS,
|
||||
appExtensionsHeaderItem,
|
||||
),
|
||||
],
|
||||
iconSrc: customerIcon,
|
||||
icon: <CustomersIcon {...iconSettings} />,
|
||||
label: intl.formatMessage(sectionNames.customers),
|
||||
permissions: [PermissionEnum.MANAGE_USERS],
|
||||
id: "customers",
|
||||
url: customerListUrl(),
|
||||
type: !isEmpty(extensions.NAVIGATION_CUSTOMERS) ? "itemGroup" : "item",
|
||||
},
|
||||
|
||||
{
|
||||
ariaLabel: "discounts",
|
||||
children: [
|
||||
{
|
||||
ariaLabel: "sales",
|
||||
label: intl.formatMessage(sectionNames.sales),
|
||||
id: "sales",
|
||||
url: saleListUrl(),
|
||||
type: "item",
|
||||
},
|
||||
{
|
||||
ariaLabel: "vouchers",
|
||||
label: intl.formatMessage(sectionNames.vouchers),
|
||||
id: "vouchers",
|
||||
url: voucherListUrl(),
|
||||
type: "item",
|
||||
},
|
||||
...mapToExtensionsItems(
|
||||
extensions.NAVIGATION_DISCOUNTS,
|
||||
appExtensionsHeaderItem,
|
||||
),
|
||||
],
|
||||
iconSrc: discountsIcon,
|
||||
icon: <VouchersIcon {...iconSettings} />,
|
||||
label: intl.formatMessage(commonMessages.discounts),
|
||||
permissions: [PermissionEnum.MANAGE_DISCOUNTS],
|
||||
id: "discounts",
|
||||
type: "itemGroup",
|
||||
},
|
||||
{
|
||||
ariaLabel: "pages",
|
||||
children: extensions.NAVIGATION_PAGES.length > 0 && [
|
||||
id: "divider-2",
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
children: !isEmpty(extensions.NAVIGATION_PAGES) && [
|
||||
{
|
||||
ariaLabel: "pages",
|
||||
label: intl.formatMessage(sectionNames.pages),
|
||||
permissions: [PermissionEnum.MANAGE_PAGES],
|
||||
id: "pages",
|
||||
url: pageListPath,
|
||||
type: "item",
|
||||
},
|
||||
...mapToExtensionsItems(
|
||||
extensions.NAVIGATION_PAGES,
|
||||
appExtensionsHeaderItem,
|
||||
),
|
||||
],
|
||||
iconSrc: pagesIcon,
|
||||
icon: <StorefrontIcon {...iconSettings} />,
|
||||
label: intl.formatMessage(sectionNames.pages),
|
||||
permissions: [PermissionEnum.MANAGE_PAGES],
|
||||
id: "pages",
|
||||
url: pageListPath,
|
||||
type: !isEmpty(extensions.NAVIGATION_PAGES) ? "itemGroup" : "item",
|
||||
},
|
||||
getAppSection(),
|
||||
{
|
||||
ariaLabel: "translations",
|
||||
children: extensions.NAVIGATION_TRANSLATIONS.length > 0 && [
|
||||
children: !isEmpty(extensions.NAVIGATION_TRANSLATIONS) && [
|
||||
{
|
||||
ariaLabel: "translations",
|
||||
label: intl.formatMessage(sectionNames.translations),
|
||||
permissions: [PermissionEnum.MANAGE_TRANSLATIONS],
|
||||
id: "translations",
|
||||
url: languageListUrl,
|
||||
type: "item",
|
||||
},
|
||||
...mapToExtensionsItems(
|
||||
extensions.NAVIGATION_TRANSLATIONS,
|
||||
appExtensionsHeaderItem,
|
||||
),
|
||||
],
|
||||
iconSrc: translationIcon,
|
||||
icon: <TranslationsIcon {...iconSettings} />,
|
||||
label: intl.formatMessage(sectionNames.translations),
|
||||
permissions: [PermissionEnum.MANAGE_TRANSLATIONS],
|
||||
id: "translations",
|
||||
url: languageListUrl,
|
||||
type: !isEmpty(extensions.NAVIGATION_TRANSLATIONS) ? "itemGroup" : "item",
|
||||
},
|
||||
{
|
||||
ariaLabel: "configure",
|
||||
iconSrc: configurationIcon,
|
||||
id: "divider-3",
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
icon: <ConfigurationIcon {...iconSettings} />,
|
||||
label: intl.formatMessage(sectionNames.configuration),
|
||||
permissions: getConfigMenuItemsPermissions(intl),
|
||||
id: "configure",
|
||||
url: configurationMenuUrl,
|
||||
type: "item",
|
||||
},
|
||||
];
|
||||
|
||||
const isMenuItemPermitted = (menuItem: FilterableMenuItem) => {
|
||||
const isMenuItemPermitted = (menuItem: SidebarMenuItem) => {
|
||||
const userPermissions = (user?.userPermissions || []).map(
|
||||
permission => permission.code,
|
||||
);
|
||||
|
@ -290,26 +300,21 @@ function useMenuStructure(
|
|||
);
|
||||
};
|
||||
|
||||
const getFilteredMenuItems = (menuItems: FilterableMenuItem[]) =>
|
||||
const getFilteredMenuItems = (menuItems: SidebarMenuItem[]) =>
|
||||
menuItems.filter(isMenuItemPermitted);
|
||||
|
||||
return [
|
||||
menuItems.reduce(
|
||||
(resultItems: FilterableMenuItem[], menuItem: FilterableMenuItem) => {
|
||||
if (!isMenuItemPermitted(menuItem)) {
|
||||
return resultItems;
|
||||
}
|
||||
const { children } = menuItem;
|
||||
const filteredChildren = children
|
||||
? getFilteredMenuItems(children)
|
||||
: undefined;
|
||||
return menuItems.reduce(
|
||||
(resultItems: SidebarMenuItem[], menuItem: SidebarMenuItem) => {
|
||||
if (!isMenuItemPermitted(menuItem)) {
|
||||
return resultItems;
|
||||
}
|
||||
const { children } = menuItem;
|
||||
const filteredChildren = children
|
||||
? getFilteredMenuItems(children)
|
||||
: undefined;
|
||||
|
||||
return [...resultItems, { ...menuItem, children: filteredChildren }];
|
||||
},
|
||||
[] as FilterableMenuItem[],
|
||||
),
|
||||
handleMenuItemClick,
|
||||
];
|
||||
return [...resultItems, { ...menuItem, children: filteredChildren }];
|
||||
},
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
export default useMenuStructure;
|
|
@ -2,19 +2,31 @@ import { getDashboardUrFromAppCompleteUrl } from "@dashboard/apps/urls";
|
|||
import { Extension } from "@dashboard/apps/useExtensions";
|
||||
import { AppExtensionMountEnum } from "@dashboard/graphql";
|
||||
import { orderDraftListUrl, orderListUrl } from "@dashboard/orders/urls";
|
||||
import { SidebarMenuItem } from "@saleor/macaw-ui";
|
||||
import { matchPath } from "react-router";
|
||||
|
||||
import { FilterableMenuItem } from "./menuStructure";
|
||||
import { SidebarMenuItem } from "./types";
|
||||
|
||||
export const mapToExtensionsItems = (
|
||||
extensions: Extension[],
|
||||
header: SidebarMenuItem,
|
||||
) => {
|
||||
const items: SidebarMenuItem[] = extensions.map(
|
||||
({ label, id, app, url, permissions, open }) => ({
|
||||
id: `extension-${id}`,
|
||||
label,
|
||||
url: getDashboardUrFromAppCompleteUrl(url, app.appUrl, app.id),
|
||||
permissions,
|
||||
onClick: open,
|
||||
type: "item",
|
||||
}),
|
||||
);
|
||||
if (items.length) {
|
||||
items.unshift(header);
|
||||
}
|
||||
return items;
|
||||
};
|
||||
|
||||
export function isMenuActive(location: string, menuItem: SidebarMenuItem) {
|
||||
if (menuItem.children) {
|
||||
return menuItem.children.reduce(
|
||||
(acc, subMenuItem) => acc || isMenuActive(location, subMenuItem),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
if (!menuItem.url) {
|
||||
return false;
|
||||
}
|
||||
|
@ -39,39 +51,19 @@ export function isMenuActive(location: string, menuItem: SidebarMenuItem) {
|
|||
});
|
||||
}
|
||||
|
||||
export const mapToExtensionsItems = (
|
||||
extensions: Extension[],
|
||||
header: FilterableMenuItem,
|
||||
) => {
|
||||
const items: FilterableMenuItem[] = extensions.map(
|
||||
({ label, id, app, url, permissions, open }) => ({
|
||||
ariaLabel: `app-${label}`,
|
||||
id: `extension-${id}`,
|
||||
label,
|
||||
url: getDashboardUrFromAppCompleteUrl(url, app.appUrl, app.id),
|
||||
onClick: open,
|
||||
permissions,
|
||||
}),
|
||||
);
|
||||
if (items.length) {
|
||||
items.unshift(header);
|
||||
}
|
||||
return items;
|
||||
};
|
||||
|
||||
const isMenuItemExtension = (menuItem: SidebarMenuItem) =>
|
||||
menuItem.id.startsWith("extension-");
|
||||
|
||||
export const getMenuItemExtension = (
|
||||
extensions: Record<AppExtensionMountEnum, Extension[]>,
|
||||
menuItem: SidebarMenuItem,
|
||||
id: string,
|
||||
) => {
|
||||
const extensionsList = Object.values(extensions).reduce(
|
||||
(list, extensions) => list.concat(extensions),
|
||||
[],
|
||||
);
|
||||
const extension = extensionsList.find(
|
||||
extension => menuItem.id === `extension-${extension.id}`,
|
||||
extension => id === `extension-${extension.id}`,
|
||||
);
|
||||
return extension;
|
||||
};
|
111
src/components/Sidebar/user/Controls.tsx
Normal file
111
src/components/Sidebar/user/Controls.tsx
Normal file
|
@ -0,0 +1,111 @@
|
|||
import { useUser } from "@dashboard/auth";
|
||||
import { isDarkTheme } from "@dashboard/misc";
|
||||
import { staffMemberDetailsUrl } from "@dashboard/staff/urls";
|
||||
import { useTheme } from "@dashboard/theme";
|
||||
import { useTheme as useLegacyTheme } from "@saleor/macaw-ui";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Dropdown,
|
||||
List,
|
||||
MoreOptionsIcon,
|
||||
sprinkles,
|
||||
Text,
|
||||
} from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { ThemeSwitcher } from "./ThemeSwitcher";
|
||||
|
||||
export const UserControls = () => {
|
||||
const { user, logout } = useUser();
|
||||
const { theme, setTheme } = useTheme();
|
||||
const { themeType: legacyThemeType, setTheme: setLegacyTheme } =
|
||||
useLegacyTheme();
|
||||
|
||||
const handleClick = () => {
|
||||
setLegacyTheme(isDarkTheme(legacyThemeType) ? "light" : "dark");
|
||||
setTheme(theme === "defaultLight" ? "defaultDark" : "defaultLight");
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown>
|
||||
<Dropdown.Trigger>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
icon={<MoreOptionsIcon />}
|
||||
data-test-id="userMenu"
|
||||
size="medium"
|
||||
/>
|
||||
</Dropdown.Trigger>
|
||||
<Dropdown.Content align="end">
|
||||
<Box __minWidth={192}>
|
||||
<List
|
||||
padding={5}
|
||||
borderRadius={4}
|
||||
boxShadow="overlay"
|
||||
backgroundColor="surfaceNeutralPlain"
|
||||
>
|
||||
<Dropdown.Item>
|
||||
<List.Item
|
||||
borderRadius={4}
|
||||
data-test-id="account-settings-button"
|
||||
>
|
||||
<Link
|
||||
to={staffMemberDetailsUrl(user?.id)}
|
||||
className={sprinkles({
|
||||
display: "block",
|
||||
width: "100%",
|
||||
...listItemStyles,
|
||||
})}
|
||||
>
|
||||
<Text>
|
||||
<FormattedMessage
|
||||
id="NQgbYA"
|
||||
defaultMessage="Account Settings"
|
||||
/>
|
||||
</Text>
|
||||
</Link>
|
||||
</List.Item>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item>
|
||||
<List.Item
|
||||
onClick={logout}
|
||||
{...listItemStyles}
|
||||
data-test-id="log-out-button"
|
||||
>
|
||||
<Text>
|
||||
<FormattedMessage
|
||||
id="qLbse5"
|
||||
defaultMessage="Log out"
|
||||
description="button"
|
||||
/>
|
||||
</Text>
|
||||
</List.Item>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item>
|
||||
<List.Item
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
gap={5}
|
||||
marginTop={3}
|
||||
onClick={handleClick}
|
||||
{...listItemStyles}
|
||||
data-test-id="theme-switch"
|
||||
>
|
||||
<ThemeSwitcher theme={theme} />
|
||||
</List.Item>
|
||||
</Dropdown.Item>
|
||||
</List>
|
||||
</Box>
|
||||
</Dropdown.Content>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
||||
const listItemStyles = {
|
||||
paddingX: 4,
|
||||
paddingY: 5,
|
||||
borderRadius: 4,
|
||||
} as const;
|
38
src/components/Sidebar/user/Info.tsx
Normal file
38
src/components/Sidebar/user/Info.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { useUser } from "@dashboard/auth";
|
||||
import { getUserInitials, getUserName } from "@dashboard/misc";
|
||||
import { Avatar, Box, Text } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
|
||||
import { UserControls } from "./Controls";
|
||||
|
||||
export const UserInfo = () => {
|
||||
const { user } = useUser();
|
||||
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
gap={6}
|
||||
paddingX={6}
|
||||
paddingY={7}
|
||||
alignItems="center"
|
||||
borderTopWidth={1}
|
||||
borderColor="neutralPlain"
|
||||
borderTopStyle="solid"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Box display="flex" gap={6} alignItems="center">
|
||||
<Avatar.User
|
||||
initials={getUserInitials(user)}
|
||||
scheme="decorative2"
|
||||
src={user?.avatar?.url}
|
||||
/>
|
||||
<Box __width={128} className="ellipsis">
|
||||
<Text variant="bodyStrong" size="small">
|
||||
{getUserName(user, true)}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<UserControls />
|
||||
</Box>
|
||||
);
|
||||
};
|
29
src/components/Sidebar/user/ThemeSwitcher.tsx
Normal file
29
src/components/Sidebar/user/ThemeSwitcher.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import {
|
||||
DarkModeIcon,
|
||||
DefaultTheme,
|
||||
LightModeIcon,
|
||||
Text,
|
||||
} from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
export const ThemeSwitcher = ({ theme }: { theme: DefaultTheme }) => {
|
||||
if (theme === "defaultLight") {
|
||||
return (
|
||||
<>
|
||||
<DarkModeIcon color="iconNeutralSubdued" />
|
||||
<Text>
|
||||
<FormattedMessage id="5ObBlW" defaultMessage="Dark Mode" />
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<LightModeIcon color="iconNeutralSubdued" />
|
||||
<Text>
|
||||
<FormattedMessage id="hVPucN" defaultMessage="Light Mode" />
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
};
|
1
src/components/Sidebar/user/index.ts
Normal file
1
src/components/Sidebar/user/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from "./Info";
|
|
@ -1,8 +1,10 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
|
||||
export interface TabContainerProps {
|
||||
children: React.ReactNode | React.ReactNodeArray;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
|
@ -19,7 +21,7 @@ const TabContainer: React.FC<TabContainerProps> = props => {
|
|||
|
||||
const classes = useStyles(props);
|
||||
|
||||
return <div className={classes.root}>{children}</div>;
|
||||
return <div className={clsx(classes.root, props.className)}>{children}</div>;
|
||||
};
|
||||
TabContainer.displayName = "TabContainer";
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { TableCell } from "@material-ui/core";
|
||||
import { TableCellProps } from "@material-ui/core/TableCell";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { vars } from "@saleor/macaw-ui/next";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
|
||||
|
@ -10,6 +11,7 @@ const useStyles = makeStyles(
|
|||
theme => ({
|
||||
arrow: {
|
||||
transition: theme.transitions.duration.short + "ms",
|
||||
marginBottom: vars.space[3],
|
||||
},
|
||||
arrowLeft: {
|
||||
marginLeft: -24,
|
||||
|
@ -33,7 +35,6 @@ const useStyles = makeStyles(
|
|||
color: theme.palette.text.primary,
|
||||
},
|
||||
display: "flex",
|
||||
height: 24,
|
||||
},
|
||||
labelContainerActive: {
|
||||
color: theme.palette.text.primary,
|
||||
|
|
|
@ -2,7 +2,7 @@ import { isExternalURL } from "@dashboard/utils/urls";
|
|||
import { TableRow, TableRowTypeMap } from "@material-ui/core";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
import React, { forwardRef } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
type MaterialTableRowPropsType = TableRowTypeMap["props"];
|
||||
|
@ -25,31 +25,28 @@ const useStyles = makeStyles(
|
|||
{ name: "TableRowLink" },
|
||||
);
|
||||
|
||||
const TableRowLink = ({
|
||||
href,
|
||||
children,
|
||||
linkClassName,
|
||||
onClick,
|
||||
...props
|
||||
}: TableRowLinkProps) => {
|
||||
const classes = useStyles();
|
||||
const TableRowLink = forwardRef<HTMLTableRowElement, TableRowLinkProps>(
|
||||
(props, ref) => {
|
||||
const { href, children, linkClassName, onClick, ...restProps } = props;
|
||||
const classes = useStyles();
|
||||
|
||||
if (!href || isExternalURL(href)) {
|
||||
return (
|
||||
<TableRow ref={ref} hover={!!onClick} onClick={onClick} {...restProps}>
|
||||
{children}
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
if (!href || isExternalURL(href)) {
|
||||
return (
|
||||
<TableRow hover={!!onClick} onClick={onClick} {...props}>
|
||||
{children}
|
||||
<TableRow ref={ref} hover={true} onClick={onClick} {...restProps}>
|
||||
<Link className={clsx(classes.link, linkClassName)} to={href}>
|
||||
{children}
|
||||
</Link>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow hover={true} onClick={onClick} {...props}>
|
||||
<Link className={clsx(classes.link, linkClassName)} to={href}>
|
||||
{children}
|
||||
</Link>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
TableRowLink.displayName = "TableRowLink";
|
||||
export default TableRowLink;
|
||||
|
|
|
@ -17,7 +17,6 @@ const useStyles = makeStyles(
|
|||
marginRight: theme.spacing(3.5),
|
||||
},
|
||||
button: {
|
||||
zIndex: 2,
|
||||
padding: `7px`,
|
||||
borderTopLeftRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
|
@ -33,15 +32,15 @@ const useStyles = makeStyles(
|
|||
"&::placeholder": {
|
||||
opacity: [[1], "!important"] as any,
|
||||
},
|
||||
zIndex: 2,
|
||||
},
|
||||
background: theme.palette.background.paper,
|
||||
},
|
||||
noteRoot: {
|
||||
left: theme.spacing(-8.5),
|
||||
marginBottom: theme.spacing(3),
|
||||
position: "relative",
|
||||
width: `calc(100% + ${theme.spacing(8.5)})`,
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: -19,
|
||||
right: 0,
|
||||
},
|
||||
noteTitle: {
|
||||
"&:last-child": {
|
||||
|
@ -49,16 +48,14 @@ const useStyles = makeStyles(
|
|||
paddingRight: 0,
|
||||
},
|
||||
alignItems: "center",
|
||||
background: theme.palette.background.default,
|
||||
display: "flex",
|
||||
paddingLeft: theme.spacing(3),
|
||||
paddingLeft: 0,
|
||||
},
|
||||
root: {
|
||||
borderColor: theme.palette.divider,
|
||||
borderStyle: "solid",
|
||||
borderWidth: "0 0 0 2px",
|
||||
marginLeft: 20,
|
||||
paddingLeft: theme.spacing(3),
|
||||
paddingTop: theme.spacing(12),
|
||||
paddingLeft: theme.spacing(3.27),
|
||||
position: "relative",
|
||||
},
|
||||
}),
|
||||
{ name: "Timeline" },
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { UserFragment } from "@dashboard/graphql";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { Typography } from "@material-ui/core";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import useMediaQuery from "@material-ui/core/useMediaQuery";
|
||||
import { makeStyles, NavigationCard } from "@saleor/macaw-ui";
|
||||
import { Box, vars } from "@saleor/macaw-ui/next";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import Container from "../components/Container";
|
||||
import PageHeader from "../components/PageHeader";
|
||||
import VersionInfo from "../components/VersionInfo";
|
||||
import { MenuSection } from "./types";
|
||||
import { hasUserMenuItemPermissions } from "./utils";
|
||||
|
@ -25,7 +27,6 @@ const useStyles = makeStyles(
|
|||
[theme.breakpoints.down("md")]: {
|
||||
gridTemplateColumns: "1fr",
|
||||
},
|
||||
borderTop: `solid 1px ${theme.palette.divider}`,
|
||||
display: "grid",
|
||||
gap: theme.spacing(4),
|
||||
gridTemplateColumns: "1fr 3fr",
|
||||
|
@ -33,9 +34,6 @@ const useStyles = makeStyles(
|
|||
},
|
||||
|
||||
configurationItem: {
|
||||
[theme.breakpoints.down("md")]: {
|
||||
gridTemplateColumns: "1fr",
|
||||
},
|
||||
display: "grid",
|
||||
gap: theme.spacing(4),
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
|
@ -59,6 +57,14 @@ const useStyles = makeStyles(
|
|||
fontSize: 20,
|
||||
fontWeight: 600 as 600,
|
||||
},
|
||||
navigationCard: {
|
||||
border: `1px solid ${vars.colors.border.neutralDefault}`,
|
||||
height: 130,
|
||||
boxShadow: "none !important",
|
||||
"& .MuiCardContent-root": {
|
||||
borderRadius: vars.borderRadius[3],
|
||||
},
|
||||
},
|
||||
}),
|
||||
{ name: "ConfigurationPage" },
|
||||
);
|
||||
|
@ -89,44 +95,50 @@ export const ConfigurationPage: React.FC<ConfigurationPageProps> = props => {
|
|||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{!isSmUp && renderVersionInfo}
|
||||
<PageHeader title={intl.formatMessage(sectionNames.configuration)}>
|
||||
<DetailedContent useSingleColumn>
|
||||
<TopNav title={intl.formatMessage(sectionNames.configuration)}>
|
||||
{isSmUp && renderVersionInfo}
|
||||
</PageHeader>
|
||||
{menus
|
||||
.filter(menu =>
|
||||
menu.menuItems.some(menuItem =>
|
||||
hasUserMenuItemPermissions(menuItem, user),
|
||||
),
|
||||
)
|
||||
.map((menu, menuIndex) => (
|
||||
<div className={classes.configurationCategory} key={menuIndex}>
|
||||
<div className={classes.configurationLabel}>
|
||||
<Typography>{menu.label}</Typography>
|
||||
</div>
|
||||
<div className={classes.configurationItem}>
|
||||
{menu.menuItems
|
||||
.filter(menuItem => hasUserMenuItemPermissions(menuItem, user))
|
||||
.map((item, itemIndex) => (
|
||||
<Link className={classes.link} to={item.url}>
|
||||
<NavigationCard
|
||||
key={itemIndex}
|
||||
icon={item.icon}
|
||||
title={item.title}
|
||||
description={item.description}
|
||||
data-test-id={
|
||||
item.testId +
|
||||
"-settings-subsection-" +
|
||||
item.title.toLowerCase()
|
||||
}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Container>
|
||||
</TopNav>
|
||||
<Content>
|
||||
<Box paddingX={9} __maxWidth={"1024px"} margin="auto">
|
||||
{menus
|
||||
.filter(menu =>
|
||||
menu.menuItems.some(menuItem =>
|
||||
hasUserMenuItemPermissions(menuItem, user),
|
||||
),
|
||||
)
|
||||
.map((menu, menuIndex) => (
|
||||
<div className={classes.configurationCategory} key={menuIndex}>
|
||||
<div className={classes.configurationLabel}>
|
||||
<Typography>{menu.label}</Typography>
|
||||
</div>
|
||||
<div className={classes.configurationItem}>
|
||||
{menu.menuItems
|
||||
.filter(menuItem =>
|
||||
hasUserMenuItemPermissions(menuItem, user),
|
||||
)
|
||||
.map((item, itemIndex) => (
|
||||
<Link className={classes.link} to={item.url}>
|
||||
<NavigationCard
|
||||
className={classes.navigationCard}
|
||||
key={itemIndex}
|
||||
icon={item.icon}
|
||||
title={item.title}
|
||||
description={item.description}
|
||||
data-test-id={
|
||||
item.testId +
|
||||
"-settings-subsection-" +
|
||||
item.title.toLowerCase()
|
||||
}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Box>
|
||||
</Content>
|
||||
</DetailedContent>
|
||||
);
|
||||
};
|
||||
ConfigurationPage.displayName = "ConfigurationPage";
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import AccountPermissions from "@dashboard/components/AccountPermissions";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import { RightSidebar } from "@dashboard/components/AppLayout/RightSidebar";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import Form from "@dashboard/components/Form";
|
||||
import Grid from "@dashboard/components/Grid";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import { CustomAppUrls } from "@dashboard/custom-apps/urls";
|
||||
import {
|
||||
|
@ -13,7 +13,6 @@ import {
|
|||
} from "@dashboard/graphql";
|
||||
import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { getFormErrors } from "@dashboard/utils/errors";
|
||||
import getAppErrorMessage from "@dashboard/utils/errors/app";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
|
@ -59,26 +58,24 @@ const CustomAppCreatePage: React.FC<CustomAppCreatePageProps> = props => {
|
|||
disabled={disabled}
|
||||
>
|
||||
{({ data, change, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink href={CustomAppUrls.resolveAppListUrl()}>
|
||||
{intl.formatMessage(sectionNames.apps)}
|
||||
</Backlink>
|
||||
<PageHeader
|
||||
<DetailedContent>
|
||||
<TopNav
|
||||
href={CustomAppUrls.resolveAppListUrl()}
|
||||
title={intl.formatMessage({
|
||||
id: "GjH9uy",
|
||||
defaultMessage: "Create New App",
|
||||
description: "header",
|
||||
})}
|
||||
/>
|
||||
<Grid>
|
||||
<div>
|
||||
<CustomAppInformation
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
</div>
|
||||
></TopNav>
|
||||
<Content>
|
||||
<CustomAppInformation
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
</Content>
|
||||
<RightSidebar>
|
||||
<AccountPermissions
|
||||
data={data}
|
||||
errorMessage={permissionsError}
|
||||
|
@ -98,14 +95,14 @@ const CustomAppCreatePage: React.FC<CustomAppCreatePageProps> = props => {
|
|||
description: "card description",
|
||||
})}
|
||||
/>
|
||||
</Grid>
|
||||
</RightSidebar>
|
||||
<Savebar
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={() => navigate(CustomAppUrls.resolveAppListUrl())}
|
||||
onSubmit={submit}
|
||||
/>
|
||||
</Container>
|
||||
</DetailedContent>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import AccountPermissions from "@dashboard/components/AccountPermissions";
|
||||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import { RightSidebar } from "@dashboard/components/AppLayout/RightSidebar";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import CardSpacer from "@dashboard/components/CardSpacer";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import Form from "@dashboard/components/Form";
|
||||
import Grid from "@dashboard/components/Grid";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import WebhooksList from "@dashboard/custom-apps/components/WebhooksList";
|
||||
import { CustomAppUrls } from "@dashboard/custom-apps/urls";
|
||||
|
@ -16,11 +16,11 @@ import {
|
|||
} from "@dashboard/graphql";
|
||||
import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { getFormErrors } from "@dashboard/utils/errors";
|
||||
import getAppErrorMessage from "@dashboard/utils/errors/app";
|
||||
import { Button, ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import SVG from "react-inlinesvg";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
||||
import activateIcon from "../../../../assets/images/activate-icon.svg";
|
||||
|
@ -104,18 +104,15 @@ const CustomAppDetailsPage: React.FC<CustomAppDetailsPageProps> = props => {
|
|||
disabled={disabled}
|
||||
>
|
||||
{({ data, change, submit, isSaveDisabled }) => (
|
||||
<Container>
|
||||
<Backlink href={CustomAppUrls.resolveAppListUrl()}>
|
||||
{intl.formatMessage(sectionNames.apps)}
|
||||
</Backlink>
|
||||
<PageHeader title={app?.name}>
|
||||
<DetailedContent>
|
||||
<TopNav href={CustomAppUrls.resolveAppListUrl()} title={app?.name}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className={classes.activateButton}
|
||||
disableFocusRipple
|
||||
onClick={data.isActive ? onAppDeactivateOpen : onAppActivateOpen}
|
||||
>
|
||||
<img src={activateIcon} alt="" />
|
||||
<SVG src={activateIcon} />
|
||||
{data?.isActive ? (
|
||||
<FormattedMessage
|
||||
id="whTEcF"
|
||||
|
@ -130,68 +127,66 @@ const CustomAppDetailsPage: React.FC<CustomAppDetailsPageProps> = props => {
|
|||
/>
|
||||
)}
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<Grid>
|
||||
<div>
|
||||
{token && (
|
||||
<>
|
||||
<CustomAppDefaultToken
|
||||
apiUrl={apiUrl}
|
||||
token={token}
|
||||
onApiUrlClick={onApiUrlClick}
|
||||
onTokenClose={onTokenClose}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</>
|
||||
)}
|
||||
<CustomAppInformation
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<CustomAppTokens
|
||||
tokens={app?.tokens}
|
||||
onCreate={onTokenCreate}
|
||||
onDelete={onTokenDelete}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<WebhooksList
|
||||
webhooks={webhooks}
|
||||
onRemove={onWebhookRemove}
|
||||
createHref={app?.isActive && webhookCreateHref}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<AccountPermissions
|
||||
data={data}
|
||||
errorMessage={permissionsError}
|
||||
disabled={disabled}
|
||||
permissions={permissions}
|
||||
permissionsExceeded={false}
|
||||
onChange={change}
|
||||
fullAccessLabel={intl.formatMessage({
|
||||
id: "D4nzdD",
|
||||
defaultMessage: "Grant this app full access to the store",
|
||||
description: "checkbox label",
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: "flP8Hj",
|
||||
defaultMessage:
|
||||
"Expand or restrict app permissions to access certain part of Saleor system.",
|
||||
description: "card description",
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
</TopNav>
|
||||
<Content>
|
||||
{token && (
|
||||
<>
|
||||
<CustomAppDefaultToken
|
||||
apiUrl={apiUrl}
|
||||
token={token}
|
||||
onApiUrlClick={onApiUrlClick}
|
||||
onTokenClose={onTokenClose}
|
||||
/>
|
||||
<CardSpacer />
|
||||
</>
|
||||
)}
|
||||
<CustomAppInformation
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<CustomAppTokens
|
||||
tokens={app?.tokens}
|
||||
onCreate={onTokenCreate}
|
||||
onDelete={onTokenDelete}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<WebhooksList
|
||||
webhooks={webhooks}
|
||||
onRemove={onWebhookRemove}
|
||||
createHref={app?.isActive && webhookCreateHref}
|
||||
/>
|
||||
</Content>
|
||||
<RightSidebar>
|
||||
<AccountPermissions
|
||||
data={data}
|
||||
errorMessage={permissionsError}
|
||||
disabled={disabled}
|
||||
permissions={permissions}
|
||||
permissionsExceeded={false}
|
||||
onChange={change}
|
||||
fullAccessLabel={intl.formatMessage({
|
||||
id: "D4nzdD",
|
||||
defaultMessage: "Grant this app full access to the store",
|
||||
description: "checkbox label",
|
||||
})}
|
||||
description={intl.formatMessage({
|
||||
id: "flP8Hj",
|
||||
defaultMessage:
|
||||
"Expand or restrict app permissions to access certain part of Saleor system.",
|
||||
description: "card description",
|
||||
})}
|
||||
/>
|
||||
</RightSidebar>
|
||||
<Savebar
|
||||
disabled={isSaveDisabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={() => navigate(CustomAppUrls.resolveAppListUrl())}
|
||||
onSubmit={submit}
|
||||
/>
|
||||
</Container>
|
||||
</DetailedContent>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
|
|
|
@ -4,9 +4,19 @@ import { FormChange } from "@dashboard/hooks/useForm";
|
|||
import { getFormErrors } from "@dashboard/utils/errors";
|
||||
import getAppErrorMessage from "@dashboard/utils/errors/app";
|
||||
import { Card, CardContent, TextField } from "@material-ui/core";
|
||||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
cardTitle: {
|
||||
paddingRight: 16,
|
||||
},
|
||||
},
|
||||
{ name: "AccountPermissions" },
|
||||
);
|
||||
|
||||
export interface CustomAppInfoProps {
|
||||
data: {
|
||||
name: string;
|
||||
|
@ -23,12 +33,14 @@ const CustomAppInformation: React.FC<CustomAppInfoProps> = ({
|
|||
onChange,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const classes = useStyles();
|
||||
|
||||
const formErrors = getFormErrors(["name"], errors);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardTitle
|
||||
className={classes.cardTitle}
|
||||
title={intl.formatMessage({
|
||||
id: "imYxM9",
|
||||
defaultMessage: "App Information",
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import { Button } from "@dashboard/components/Button";
|
||||
import CardTitle from "@dashboard/components/CardTitle";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import { TableButtonWrapper } from "@dashboard/components/TableButtonWrapper/TableButtonWrapper";
|
||||
import TableRowLink from "@dashboard/components/TableRowLink";
|
||||
import { CustomAppUrls } from "@dashboard/custom-apps/urls";
|
||||
import { AppListItemFragment } from "@dashboard/graphql";
|
||||
import { commonMessages, sectionNames } from "@dashboard/intl";
|
||||
import { sectionNames } from "@dashboard/intl";
|
||||
import { renderCollection } from "@dashboard/misc";
|
||||
import { Card, TableBody, TableCell, Typography } from "@material-ui/core";
|
||||
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";
|
||||
|
||||
|
@ -32,86 +32,87 @@ const CustomAppListPage: React.FC<CustomAppListPageProps> = ({
|
|||
const classes = useStyles({});
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<PageHeader title={intl.formatMessage(sectionNames.webhooksAndEvents)} />
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Local apps are custom webhooks & token pairs that can be used to
|
||||
connect apps and access Saleor API."
|
||||
id="L/sNGY"
|
||||
/>
|
||||
</p>
|
||||
<Card className={classes.customApps}>
|
||||
<CardTitle
|
||||
toolbar={
|
||||
<Button
|
||||
variant="secondary"
|
||||
href={CustomAppUrls.appAddUrl}
|
||||
data-test-id="create-app"
|
||||
>
|
||||
<>
|
||||
<TopNav title={intl.formatMessage(sectionNames.webhooksAndEvents)}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
href={CustomAppUrls.appAddUrl}
|
||||
data-test-id="create-app"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="XB2Jj9"
|
||||
defaultMessage="Create App"
|
||||
description="create app button"
|
||||
/>
|
||||
</Button>
|
||||
</TopNav>
|
||||
<Content>
|
||||
<Box padding={9}>
|
||||
<Box marginBottom={4}>
|
||||
<Text as="p">
|
||||
<FormattedMessage
|
||||
id="XB2Jj9"
|
||||
defaultMessage="Create App"
|
||||
description="create app button"
|
||||
defaultMessage="Local apps are custom webhooks & token pairs that can be used to
|
||||
connect apps and access Saleor API."
|
||||
id="L/sNGY"
|
||||
/>
|
||||
</Button>
|
||||
}
|
||||
title={intl.formatMessage(commonMessages.customApps)}
|
||||
/>
|
||||
<ResponsiveTable>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
appsList,
|
||||
(app, index) =>
|
||||
app ? (
|
||||
<TableRowLink
|
||||
key={app.id}
|
||||
className={classes.tableRow}
|
||||
href={getCustomAppHref(app.id)}
|
||||
>
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<ResponsiveTable>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
appsList,
|
||||
(app, index) =>
|
||||
app ? (
|
||||
<TableRowLink
|
||||
key={app.id}
|
||||
className={classes.tableRow}
|
||||
href={getCustomAppHref(app.id)}
|
||||
>
|
||||
<TableCell className={classes.colName}>
|
||||
<span data-tc="name" className={classes.appName}>
|
||||
{app.name}
|
||||
</span>
|
||||
{!app.isActive && (
|
||||
<div className={classes.statusWrapper}>
|
||||
<DeactivatedText />
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAction}>
|
||||
<TableButtonWrapper>
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
color="primary"
|
||||
onClick={() => onRemove(app.id)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableButtonWrapper>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
) : (
|
||||
<AppsSkeleton key={index} />
|
||||
),
|
||||
() => (
|
||||
<TableRowLink className={classes.tableRow}>
|
||||
<TableCell className={classes.colName}>
|
||||
<span data-tc="name" className={classes.appName}>
|
||||
{app.name}
|
||||
</span>
|
||||
{!app.isActive && (
|
||||
<div className={classes.statusWrapper}>
|
||||
<DeactivatedText />
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colAction}>
|
||||
<TableButtonWrapper>
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
color="primary"
|
||||
onClick={() => onRemove(app.id)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableButtonWrapper>
|
||||
<Typography className={classes.text} variant="body2">
|
||||
<FormattedMessage
|
||||
id="voRaz3"
|
||||
defaultMessage="Your custom-created apps will be shown here."
|
||||
description="custom apps content"
|
||||
/>
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
) : (
|
||||
<AppsSkeleton key={index} />
|
||||
),
|
||||
() => (
|
||||
<TableRowLink className={classes.tableRow}>
|
||||
<TableCell className={classes.colName}>
|
||||
<Typography className={classes.text} variant="body2">
|
||||
<FormattedMessage
|
||||
id="voRaz3"
|
||||
defaultMessage="Your custom-created apps will be shown here."
|
||||
description="custom apps content"
|
||||
/>
|
||||
</Typography>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</Card>
|
||||
</Container>
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</Box>
|
||||
</Content>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -5,7 +5,13 @@ import Skeleton from "@dashboard/components/Skeleton";
|
|||
import TableRowLink from "@dashboard/components/TableRowLink";
|
||||
import { AppUpdateMutation } from "@dashboard/graphql";
|
||||
import { renderCollection } from "@dashboard/misc";
|
||||
import { Card, TableBody, TableCell, TableHead } from "@material-ui/core";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
} from "@material-ui/core";
|
||||
import { DeleteIcon, IconButton } from "@saleor/macaw-ui";
|
||||
import React from "react";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
|
@ -33,6 +39,7 @@ const CustomAppTokens: React.FC<CustomAppTokensProps> = props => {
|
|||
defaultMessage: "Tokens",
|
||||
description: "header",
|
||||
})}
|
||||
className={classes.cardTitle}
|
||||
toolbar={
|
||||
<Button
|
||||
variant="secondary"
|
||||
|
@ -47,63 +54,69 @@ const CustomAppTokens: React.FC<CustomAppTokensProps> = props => {
|
|||
</Button>
|
||||
}
|
||||
/>
|
||||
<ResponsiveTable>
|
||||
<TableHead>
|
||||
<TableRowLink>
|
||||
<TableCell className={classes.colNote}>
|
||||
<FormattedMessage id="0DRBjg" defaultMessage="Token Note" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colKey}>
|
||||
<FormattedMessage
|
||||
id="MAsLIT"
|
||||
defaultMessage="Key"
|
||||
description="custom app token key"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colActions}>
|
||||
<FormattedMessage
|
||||
id="VHuzgq"
|
||||
defaultMessage="Actions"
|
||||
description="table actions"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
tokens,
|
||||
token => (
|
||||
<TableRowLink key={token ? token.id : "skeleton"}>
|
||||
<TableCell className={classes.colNote}>
|
||||
{token?.name || <Skeleton />}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colKey}>
|
||||
{token?.authToken ? `**** ${token.authToken}` : <Skeleton />}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colActions}>
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
color="primary"
|
||||
onClick={() => onDelete(token.id)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
() => (
|
||||
<TableRowLink>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage
|
||||
id="bsP4f3"
|
||||
defaultMessage="No tokens found"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
<CardContent>
|
||||
<ResponsiveTable>
|
||||
<TableHead>
|
||||
<TableRowLink>
|
||||
<TableCell className={classes.colNote}>
|
||||
<FormattedMessage id="0DRBjg" defaultMessage="Token Note" />
|
||||
</TableCell>
|
||||
<TableCell className={classes.colKey}>
|
||||
<FormattedMessage
|
||||
id="MAsLIT"
|
||||
defaultMessage="Key"
|
||||
description="custom app token key"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.colActions}>
|
||||
<FormattedMessage
|
||||
id="VHuzgq"
|
||||
defaultMessage="Actions"
|
||||
description="table actions"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{renderCollection(
|
||||
tokens,
|
||||
token => (
|
||||
<TableRowLink key={token ? token.id : "skeleton"}>
|
||||
<TableCell className={classes.colNote}>
|
||||
{token?.name || <Skeleton />}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colKey}>
|
||||
{token?.authToken ? (
|
||||
`**** ${token.authToken}`
|
||||
) : (
|
||||
<Skeleton />
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className={classes.colActions}>
|
||||
<IconButton
|
||||
variant="secondary"
|
||||
color="primary"
|
||||
onClick={() => onDelete(token.id)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
() => (
|
||||
<TableRowLink>
|
||||
<TableCell colSpan={numberOfColumns}>
|
||||
<FormattedMessage
|
||||
id="bsP4f3"
|
||||
defaultMessage="No tokens found"
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRowLink>
|
||||
),
|
||||
)}
|
||||
</TableBody>
|
||||
</ResponsiveTable>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -69,7 +69,7 @@ const TokenCreateDialog: React.FC<TokenCreateDialogProps> = props => {
|
|||
<Form initial={{ name: "" }} onSubmit={data => onCreate(data.name)}>
|
||||
{({ change, data, submit }) => (
|
||||
<>
|
||||
<DialogTitle>
|
||||
<DialogTitle disableTypography>
|
||||
<FormattedMessage
|
||||
id="T5nU7u"
|
||||
defaultMessage="Create Token"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Backlink } from "@dashboard/components/Backlink";
|
||||
import Container from "@dashboard/components/Container";
|
||||
import { Content } from "@dashboard/components/AppLayout/Content";
|
||||
import { DetailedContent } from "@dashboard/components/AppLayout/DetailedContent";
|
||||
import { TopNav } from "@dashboard/components/AppLayout/TopNav";
|
||||
import Form from "@dashboard/components/Form";
|
||||
import FormSpacer from "@dashboard/components/FormSpacer";
|
||||
import PageHeader from "@dashboard/components/PageHeader";
|
||||
import Savebar from "@dashboard/components/Savebar";
|
||||
import WebhookEvents from "@dashboard/custom-apps/components/WebhookEvents";
|
||||
import WebhookInfo from "@dashboard/custom-apps/components/WebhookInfo";
|
||||
|
@ -21,11 +21,12 @@ import {
|
|||
import { SubmitPromise } from "@dashboard/hooks/useForm";
|
||||
import useNavigator from "@dashboard/hooks/useNavigator";
|
||||
import { ConfirmButtonTransitionState } from "@saleor/macaw-ui";
|
||||
import { Box } from "@saleor/macaw-ui/next";
|
||||
import { parse, print } from "graphql";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useIntl } from "react-intl";
|
||||
|
||||
import WebhookSubscriptionQuery from "../WebhookSubscriptionQuery/WebhookSubscriptionQuery";
|
||||
import WebhookSubscriptionQuery from "../WebhookSubscriptionQuery";
|
||||
import { getHeaderTitle } from "./messages";
|
||||
|
||||
export interface WebhookFormData {
|
||||
|
@ -50,7 +51,6 @@ export interface WebhookDetailsPageProps {
|
|||
|
||||
const WebhookDetailsPage: React.FC<WebhookDetailsPageProps> = ({
|
||||
appId,
|
||||
appName,
|
||||
disabled,
|
||||
errors,
|
||||
webhook,
|
||||
|
@ -105,40 +105,41 @@ const WebhookDetailsPage: React.FC<WebhookDetailsPageProps> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Backlink href={backUrl}>{appName}</Backlink>
|
||||
<PageHeader title={getHeaderTitle(intl, webhook)}>
|
||||
<WebhookStatus
|
||||
data={data.isActive}
|
||||
disabled={disabled}
|
||||
onChange={change}
|
||||
/>
|
||||
</PageHeader>
|
||||
<WebhookInfo
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<FormSpacer />
|
||||
<WebhookEvents
|
||||
data={data}
|
||||
onSyncEventChange={handleSyncEventsSelect}
|
||||
onAsyncEventChange={handleAsyncEventsSelect}
|
||||
/>
|
||||
<FormSpacer />
|
||||
<WebhookSubscriptionQuery
|
||||
query={query}
|
||||
setQuery={setQuery}
|
||||
data={data}
|
||||
/>
|
||||
<DetailedContent useSingleColumn>
|
||||
<TopNav href={backUrl} title={getHeaderTitle(intl, webhook)} />
|
||||
<Content>
|
||||
<Box paddingX={9}>
|
||||
<WebhookStatus
|
||||
data={data.isActive}
|
||||
disabled={disabled}
|
||||
onChange={change}
|
||||
/>
|
||||
<WebhookInfo
|
||||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
onChange={change}
|
||||
/>
|
||||
<FormSpacer />
|
||||
<WebhookEvents
|
||||
data={data}
|
||||
onSyncEventChange={handleSyncEventsSelect}
|
||||
onAsyncEventChange={handleAsyncEventsSelect}
|
||||
/>
|
||||
<WebhookSubscriptionQuery
|
||||
query={query}
|
||||
setQuery={setQuery}
|
||||
data={data}
|
||||
/>
|
||||
</Box>
|
||||
</Content>
|
||||
<Savebar
|
||||
disabled={disabled}
|
||||
state={saveButtonBarState}
|
||||
onCancel={() => navigate(backUrl)}
|
||||
onSubmit={submit}
|
||||
/>
|
||||
</Container>
|
||||
</DetailedContent>
|
||||
);
|
||||
}}
|
||||
</Form>
|
||||
|
|
|
@ -72,7 +72,7 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
|
|||
return (
|
||||
<>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<CardContent className={classes.card}>
|
||||
<PageTabs value={tab} onChange={handleTabChange}>
|
||||
<PageTab
|
||||
label={intl.formatMessage(messages.asynchronous)}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { makeStyles } from "@saleor/macaw-ui";
|
||||
import { vars } from "@saleor/macaw-ui/next";
|
||||
|
||||
export const useStyles = makeStyles(
|
||||
theme => ({
|
||||
|
@ -8,7 +9,7 @@ export const useStyles = makeStyles(
|
|||
},
|
||||
objectsWrapper: {
|
||||
borderRight: "1px solid",
|
||||
borderRightColor: theme.palette.divider,
|
||||
borderRightColor: vars.colors.border.neutralPlain,
|
||||
padding: theme.spacing(3),
|
||||
},
|
||||
listHeader: {
|
||||
|
@ -18,7 +19,7 @@ export const useStyles = makeStyles(
|
|||
},
|
||||
listBody: {
|
||||
height: 300,
|
||||
overflow: "scroll",
|
||||
overflowY: "auto",
|
||||
},
|
||||
listItem: {
|
||||
minHeight: 0,
|
||||
|
@ -32,6 +33,9 @@ export const useStyles = makeStyles(
|
|||
checkbox: {
|
||||
padding: 0,
|
||||
},
|
||||
card: {
|
||||
paddingLeft: 0,
|
||||
},
|
||||
}),
|
||||
{ name: "WebhookEvents" },
|
||||
);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue