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:
Patryk Andrzejewski 2023-02-20 16:21:28 +01:00 committed by GitHub
parent 91bd9c772d
commit 3789f5bb52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
279 changed files with 11146 additions and 21463 deletions

1
.gitignore vendored
View file

@ -21,7 +21,6 @@
!.travis*
!.tx
!.husky
*.css
*.log
*.pyc
*.mo

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -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);

View file

@ -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(() => {

View file

@ -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']";

View file

@ -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"]',
};

View file

@ -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']",

View file

@ -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']",

View file

@ -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)

View file

@ -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 dont have assigned discounts will use their parent channel to define the value. Value will be converted to channels 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

File diff suppressed because it is too large Load diff

View file

@ -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"
}

View file

@ -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>
</>
);
};

View file

@ -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,7 +43,8 @@ export const AppInstallPage: React.FC<AppInstallPageProps> = ({
const name = data?.name || "";
return (
<Container>
<DetailedContent useSingleColumn constHeight>
<Content>
<CardSpacer />
<Card>
<CardTitle
@ -58,17 +65,20 @@ export const AppInstallPage: React.FC<AppInstallPageProps> = ({
/>
<CardContent className={classes.installCard}>
{loading ? (
<Skeleton />
<CircularProgress />
) : (
<div className={classes.installAppContainer}>
<div
className={clsx(classes.installIcon, classes.installSaleorIcon)}
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>
<GenericAppIcon />
</div>
</div>
)}
@ -76,6 +86,7 @@ export const AppInstallPage: React.FC<AppInstallPageProps> = ({
</Card>
<CardSpacer />
<Card>
{!loading && (
<CardTitle
title={intl.formatMessage({
id: "VsGcdP",
@ -83,6 +94,7 @@ export const AppInstallPage: React.FC<AppInstallPageProps> = ({
description: "section header",
})}
/>
)}
<CardContent>
{loading ? (
<Skeleton />
@ -133,13 +145,10 @@ export const AppInstallPage: React.FC<AppInstallPageProps> = ({
</CardContent>
</Card>
<CardSpacer />
<Grid container justify="space-between">
<Grid xs={6} item>
<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>
);
};

View file

@ -1,4 +1,3 @@
import Container from "@dashboard/components/Container";
import { AppQuery } from "@dashboard/graphql";
import React from "react";
@ -21,7 +20,6 @@ export const AppPage: React.FC<AppPageProps> = ({
const classes = useStyles();
return (
<Container className={classes.container}>
<div className={classes.iframeContainer}>
{url && (
<AppFrame
@ -33,7 +31,6 @@ export const AppPage: React.FC<AppPageProps> = ({
/>
)}
</div>
</Container>
);
};

View file

@ -10,7 +10,7 @@ export const useStyles = makeStyles(
height: "100%",
"& > iframe": {
border: "none",
minHeight: "60vh",
minHeight: "100vh",
height: "100%",
width: "100%",
},

View file

@ -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>
</>
);
};

View file

@ -4,6 +4,7 @@ export const useStyles = makeStyles(
theme => ({
[theme.breakpoints.up("lg")]: {
colName: {
paddingLeft: "0 !important",
"&&": {
width: "auto",
},

View file

@ -64,10 +64,8 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
installations.filter(item => item.id !== id),
);
const {
data: appsInProgressData,
refetch: appsInProgressRefetch,
} = useAppsInstallationsQuery({
const { data: appsInProgressData, refetch: appsInProgressRefetch } =
useAppsInstallationsQuery({
displayLoader: false,
});
const { data, loading, refetch } = useAppsListQuery({
@ -121,10 +119,8 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
AppListUrlQueryParams
>(navigate, appsListUrl, params);
const [
deleteInProgressApp,
deleteInProgressAppOpts,
] = useAppDeleteFailedInstallationMutation({
const [deleteInProgressApp, deleteInProgressAppOpts] =
useAppDeleteFailedInstallationMutation({
onCompleted: data => {
if (!data?.appDeleteFailedInstallation?.errors?.length) {
removeAppNotify();

View file

@ -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";

View file

@ -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,23 +173,20 @@ 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>
></TopNav>
<Content>
<AttributeDetails
canChangeType={attribute === null}
data={data}
@ -209,7 +206,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
<AttributeValues
inputType={data.inputType}
disabled={disabled}
values={mapEdgesToItems(values) ?? []}
values={mapEdgesToItems(values)}
onValueAdd={onValueAdd}
onValueDelete={onValueDelete}
onValueReorder={onValueReorder}
@ -224,8 +221,8 @@ const AttributePage: React.FC<AttributePageProps> = ({
)}
<CardSpacer />
<Metadata data={data} onChange={changeMetadata} />
</div>
<div>
</Content>
<RightSidebar>
<AttributeOrganization
canChangeType={attribute === null}
data={data}
@ -239,8 +236,7 @@ const AttributePage: React.FC<AttributePageProps> = ({
disabled={disabled}
onChange={change}
/>
</div>
</Grid>
</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)}
</>
);

View file

@ -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"

View file

@ -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",

View file

@ -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,18 +36,17 @@ 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>
<Content>
<Box height="100vh" __marginBottom="auto">
<CategoryDetailsForm
data={data}
disabled={disabled}
@ -74,14 +73,15 @@ export const CategoryCreatePage: React.FC<CategoryCreatePageProps> = ({
/>
<CardSpacer />
<Metadata data={data} onChange={handlers.changeMetadata} />
</Box>
</Content>
<Savebar
onCancel={() => navigate(backUrl)}
onSubmit={submit}
state={saveButtonBarState}
disabled={isSaveDisabled}
/>
</div>
</Container>
</DetailedContent>
)}
</CategoryCreateForm>
);

View file

@ -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"

View file

@ -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";

View file

@ -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,11 +91,9 @@ 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} />
<DetailedContent>
<TopNav href={backHref} title={category?.name} />
<Content>
<CategoryDetailsForm
data={data}
disabled={disabled}
@ -131,7 +129,7 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
<CardSpacer />
<Metadata data={data} onChange={handlers.changeMetadata} />
<CardSpacer />
<TabContainer>
<TabContainer className={sprinkles({ paddingX: 9 })}>
<CategoriesTab
isActive={currentTab === CategoryPageTab.categories}
changeTab={changeTab}
@ -211,7 +209,8 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
state={saveButtonBarState}
disabled={isSaveDisabled}
/>
</Container>
</Content>
</DetailedContent>
)}
</CategoryUpdateForm>
);

View file

@ -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,7 +163,8 @@ const ChannelDetailsPage = function<TErrors extends ChannelErrorFragment[]>({
setSelectedCurrencyCode,
currencyCodes,
);
const handleDefaultCountrySelect = createSingleAutocompleteSelectHandler(
const handleDefaultCountrySelect =
createSingleAutocompleteSelectHandler(
change,
setSelectedCountryDisplayName,
countryChoices,
@ -194,9 +198,19 @@ const ChannelDetailsPage = function<TErrors extends ChannelErrorFragment[]>({
const allErrors = [...errors, ...validationErrors];
return (
<>
<Grid>
<div>
<DetailedContent>
<TopNav
href={channelsListUrl()}
title={
channel?.name ||
intl.formatMessage({
id: "DnghuS",
defaultMessage: "New Channel",
description: "channel create",
})
}
/>
<Content>
<ChannelForm
data={data}
disabled={disabled}
@ -209,8 +223,8 @@ const ChannelDetailsPage = function<TErrors extends ChannelErrorFragment[]>({
onDefaultCountryChange={handleDefaultCountrySelect}
errors={allErrors}
/>
</div>
<div>
</Content>
<RightSidebar>
{!!updateChannelStatus && (
<>
<ChannelStatus
@ -265,8 +279,7 @@ const ChannelDetailsPage = function<TErrors extends ChannelErrorFragment[]>({
disabled={disabled}
onChange={change}
/>
</div>
</Grid>
</RightSidebar>
<Savebar
onCancel={() => navigate(channelsListUrl())}
onSubmit={submit}
@ -274,7 +287,7 @@ const ChannelDetailsPage = function<TErrors extends ChannelErrorFragment[]>({
state={saveButtonBarState}
disabled={disabled}
/>
</>
</DetailedContent>
);
}}
</Form>

View file

@ -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>
</>
);
};

View file

@ -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>
</>
</>
);
};

View file

@ -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,11 +226,6 @@ 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={
@ -277,7 +268,6 @@ export const ChannelDetails: React.FC<ChannelDetailsProps> = ({
saveButtonBarState={updateChannelOpts.status}
countries={shop?.countries || []}
/>
</Container>
<ChannelDeleteDialog
channelsChoices={channelsChoices}
hasOrders={data?.channel?.hasOrders}

View file

@ -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,19 +58,16 @@ 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>
<Content>
<CollectionDetails
data={data}
disabled={disabled}
@ -133,8 +129,8 @@ const CollectionCreatePage: React.FC<CollectionCreatePageProps> = ({
/>
<CardSpacer />
<Metadata data={data} onChange={handlers.changeMetadata} />
</div>
<div>
</Content>
<RightSidebar>
<ChannelsAvailabilityCard
messages={{
hiddenLabel: intl.formatMessage({
@ -157,15 +153,14 @@ const CollectionCreatePage: React.FC<CollectionCreatePageProps> = ({
onChange={handlers.changeChannels}
openModal={openChannelsModal}
/>
</div>
</Grid>
</RightSidebar>
<Savebar
state={saveButtonBarState}
disabled={isSaveDisabled}
onCancel={() => navigate(collectionListUrl())}
onSubmit={submit}
/>
</Container>
</DetailedContent>
)}
</CollectionCreateForm>
);

View file

@ -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,13 +75,9 @@ 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>
<div>
<DetailedContent>
<TopNav href={collectionListUrl()} title={collection?.name} />
<Content>
<CollectionDetails
data={data}
disabled={disabled}
@ -122,8 +117,8 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
titlePlaceholder={collection?.name}
onChange={change}
/>
</div>
<div>
</Content>
<RightSidebar>
<div>
<ChannelsAvailabilityCard
managePermissions={[PermissionEnum.MANAGE_PRODUCTS]}
@ -148,8 +143,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
openModal={openChannelsModal}
/>
</div>
</div>
</Grid>
</RightSidebar>
<Savebar
state={saveButtonBarState}
disabled={isSaveDisabled}
@ -157,7 +151,7 @@ const CollectionDetailsPage: React.FC<CollectionDetailsPageProps> = ({
onDelete={onCollectionRemove}
onSubmit={submit}
/>
</Container>
</DetailedContent>
)}
</CollectionUpdateForm>
);

View file

@ -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",

View file

@ -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";

View file

@ -98,7 +98,7 @@ const AccountPermissions: React.FC<AccountPermissionsProps> = props => {
/>
{permissionsExceeded && (
<>
<CardContent>
<CardContent style={{ paddingLeft: 0 }}>
<Typography variant="body2">
{intl.formatMessage({
id: "MVU6ol",

View file

@ -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>

View file

@ -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",
},

View file

@ -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}
/>
)}
<div
className={clsx(classes.content, {
[fullSizeClasses.content]: fullSize,
})}
>
{appState.loading ? (
<Box display="grid" __gridTemplateColumns="auto 1fr">
{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,
})}
<Box
height="100vh"
borderColor="neutralPlain"
borderRightWidth={1}
backgroundColor="subdued"
borderStyle="solid"
position="sticky"
top={0}
borderLeftWidth={0}
borderTopWidth={0}
borderBottomWidth={0}
>
<Sidebar />
</Box>
<Box height="100%" width="100%">
<Box as="main" width="100%">
{children}
</main>
</div>
<div className={classes.appAction} ref={appActionAnchor} />
</div>
</div>
</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>
</>
);
};

View 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>
);

View 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>
);
};

View 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>
);

View 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>
);

View file

@ -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";

View 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>
);
};

View file

@ -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";

View file

@ -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")]: {

View file

@ -123,7 +123,7 @@ const AssignAttributeDialog: React.FC<AssignAttributeDialogProps> = ({
paper: classes.dialogPaper,
}}
>
<DialogTitle>
<DialogTitle disableTypography>
<FormattedMessage {...messages.title} />
</DialogTitle>
<DialogContent className={classes.searchArea}>

View file

@ -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"

View file

@ -108,7 +108,7 @@ const AssignProductDialog: React.FC<AssignProductDialogProps> = props => {
fullWidth
maxWidth="sm"
>
<DialogTitle>
<DialogTitle disableTypography>
<FormattedMessage {...messages.assignVariantDialogHeader} />
</DialogTitle>
<DialogContent>

View file

@ -94,7 +94,7 @@ const AssignVariantDialog: React.FC<AssignVariantDialogProps> = props => {
fullWidth
maxWidth="sm"
>
<DialogTitle>
<DialogTitle disableTypography>
<FormattedMessage {...messages.assignVariantDialogHeader} />
</DialogTitle>
<DialogContent>

View file

@ -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={() =>

View file

@ -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={{

View file

@ -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,13 +34,14 @@ 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": {
"& input::-webkit-outer-spin-button, input::-webkit-inner-spin-button":
{
appearance: "none",
margin: 0,
},
@ -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],
);

View file

@ -42,7 +42,7 @@ const useStyles = makeStyles(
root: {
alignItems: "center",
display: "flex",
height: "calc(100vh - 180px)",
height: "100vh",
},
header: {
marginBottom: theme.spacing(2),

View file

@ -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}

View file

@ -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,10 +120,8 @@ const FilterContent: React.FC<FilterContentProps> = ({
{},
);
const [
autocompleteDisplayValues,
setAutocompleteDisplayValues,
] = useStateFromProps<FilterAutocompleteDisplayValues>(
const [autocompleteDisplayValues, setAutocompleteDisplayValues] =
useStateFromProps<FilterAutocompleteDisplayValues>(
initialAutocompleteDisplayValues,
);
@ -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 => {

View file

@ -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),
},

View file

@ -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),

View file

@ -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: {

View file

@ -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" },
);

View file

@ -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",

View file

@ -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,
className: clsx(
{
[classes.root]: inline,
[classes[color]]: true,
[classes.underline]: underline,
[classes.noUnderline]: !underline,
[classes.disabled]: disabled,
}),
},
className,
),
onClick: event => {
if (disabled || !onClick) {
return;

View file

@ -31,6 +31,7 @@ const useStyles = makeStyles(
"&&": {
paddingBottom: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingLeft: theme.spacing(4),
},
},
content: {

View file

@ -43,7 +43,7 @@ const useStyles = makeStyles(
root: {
alignItems: "center",
display: "flex",
height: "calc(100vh - 180px)",
height: "100vh",
},
}),
{ name: "NotFoundPage" },

View file

@ -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>
);
};

View file

@ -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: {

View file

@ -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"

View file

@ -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;

View 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>
);

View 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>
);

View 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>
</>
);

View file

@ -0,0 +1 @@
export * from "./Sidebar";

View 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>
);

View 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} />;
}
};

View 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>
);

View 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>
);
};

View 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>
);
};

View file

@ -0,0 +1 @@
export * from "./Menu";

View 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"];
}

View file

@ -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,12 +300,11 @@ function useMenuStructure(
);
};
const getFilteredMenuItems = (menuItems: FilterableMenuItem[]) =>
const getFilteredMenuItems = (menuItems: SidebarMenuItem[]) =>
menuItems.filter(isMenuItemPermitted);
return [
menuItems.reduce(
(resultItems: FilterableMenuItem[], menuItem: FilterableMenuItem) => {
return menuItems.reduce(
(resultItems: SidebarMenuItem[], menuItem: SidebarMenuItem) => {
if (!isMenuItemPermitted(menuItem)) {
return resultItems;
}
@ -306,10 +315,6 @@ function useMenuStructure(
return [...resultItems, { ...menuItem, children: filteredChildren }];
},
[] as FilterableMenuItem[],
),
handleMenuItemClick,
];
[],
);
}
export default useMenuStructure;

View file

@ -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;
};

View 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;

View 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>
);
};

View 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>
</>
);
};

View file

@ -0,0 +1 @@
export * from "./Info";

View file

@ -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";

View file

@ -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,

View file

@ -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 TableRowLink = forwardRef<HTMLTableRowElement, TableRowLinkProps>(
(props, ref) => {
const { href, children, linkClassName, onClick, ...restProps } = props;
const classes = useStyles();
if (!href || isExternalURL(href)) {
return (
<TableRow hover={!!onClick} onClick={onClick} {...props}>
<TableRow ref={ref} hover={!!onClick} onClick={onClick} {...restProps}>
{children}
</TableRow>
);
}
return (
<TableRow hover={true} onClick={onClick} {...props}>
<TableRow ref={ref} hover={true} onClick={onClick} {...restProps}>
<Link className={clsx(classes.link, linkClassName)} to={href}>
{children}
</Link>
</TableRow>
);
};
},
);
TableRowLink.displayName = "TableRowLink";
export default TableRowLink;

View file

@ -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" },

View file

@ -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,11 +95,12 @@ 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>
</TopNav>
<Content>
<Box paddingX={9} __maxWidth={"1024px"} margin="auto">
{menus
.filter(menu =>
menu.menuItems.some(menuItem =>
@ -107,10 +114,13 @@ export const ConfigurationPage: React.FC<ConfigurationPageProps> = props => {
</div>
<div className={classes.configurationItem}>
{menu.menuItems
.filter(menuItem => hasUserMenuItemPermissions(menuItem, user))
.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}
@ -126,7 +136,9 @@ export const ConfigurationPage: React.FC<ConfigurationPageProps> = props => {
</div>
</div>
))}
</Container>
</Box>
</Content>
</DetailedContent>
);
};
ConfigurationPage.displayName = "ConfigurationPage";

View file

@ -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>
></TopNav>
<Content>
<CustomAppInformation
data={data}
disabled={disabled}
errors={errors}
onChange={change}
/>
</div>
</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>
);

View file

@ -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,9 +127,8 @@ const CustomAppDetailsPage: React.FC<CustomAppDetailsPageProps> = props => {
/>
)}
</Button>
</PageHeader>
<Grid>
<div>
</TopNav>
<Content>
{token && (
<>
<CustomAppDefaultToken
@ -162,8 +158,8 @@ const CustomAppDetailsPage: React.FC<CustomAppDetailsPageProps> = props => {
onRemove={onWebhookRemove}
createHref={app?.isActive && webhookCreateHref}
/>
</div>
<div>
</Content>
<RightSidebar>
<AccountPermissions
data={data}
errorMessage={permissionsError}
@ -183,15 +179,14 @@ const CustomAppDetailsPage: React.FC<CustomAppDetailsPageProps> = props => {
description: "card description",
})}
/>
</div>
</Grid>
</RightSidebar>
<Savebar
disabled={isSaveDisabled}
state={saveButtonBarState}
onCancel={() => navigate(CustomAppUrls.resolveAppListUrl())}
onSubmit={submit}
/>
</Container>
</DetailedContent>
)}
</Form>
);

View file

@ -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",

View file

@ -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,18 +32,8 @@ 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={
<>
<TopNav title={intl.formatMessage(sectionNames.webhooksAndEvents)}>
<Button
variant="secondary"
href={CustomAppUrls.appAddUrl}
@ -55,9 +45,19 @@ const CustomAppListPage: React.FC<CustomAppListPageProps> = ({
description="create app button"
/>
</Button>
}
title={intl.formatMessage(commonMessages.customApps)}
</TopNav>
<Content>
<Box padding={9}>
<Box marginBottom={4}>
<Text as="p">
<FormattedMessage
defaultMessage="Local apps are custom webhooks & token pairs that can be used to
connect apps and access Saleor API."
id="L/sNGY"
/>
</Text>
</Box>
<ResponsiveTable>
<TableBody>
{renderCollection(
@ -110,8 +110,9 @@ const CustomAppListPage: React.FC<CustomAppListPageProps> = ({
)}
</TableBody>
</ResponsiveTable>
</Card>
</Container>
</Box>
</Content>
</>
);
};

View file

@ -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,6 +54,7 @@ const CustomAppTokens: React.FC<CustomAppTokensProps> = props => {
</Button>
}
/>
<CardContent>
<ResponsiveTable>
<TableHead>
<TableRowLink>
@ -78,7 +86,11 @@ const CustomAppTokens: React.FC<CustomAppTokensProps> = props => {
{token?.name || <Skeleton />}
</TableCell>
<TableCell className={classes.colKey}>
{token?.authToken ? `**** ${token.authToken}` : <Skeleton />}
{token?.authToken ? (
`**** ${token.authToken}`
) : (
<Skeleton />
)}
</TableCell>
<TableCell className={classes.colActions}>
<IconButton
@ -104,6 +116,7 @@ const CustomAppTokens: React.FC<CustomAppTokensProps> = props => {
)}
</TableBody>
</ResponsiveTable>
</CardContent>
</Card>
);
};

View file

@ -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"

View file

@ -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,15 +105,15 @@ const WebhookDetailsPage: React.FC<WebhookDetailsPageProps> = ({
);
return (
<Container>
<Backlink href={backUrl}>{appName}</Backlink>
<PageHeader title={getHeaderTitle(intl, webhook)}>
<DetailedContent useSingleColumn>
<TopNav href={backUrl} title={getHeaderTitle(intl, webhook)} />
<Content>
<Box paddingX={9}>
<WebhookStatus
data={data.isActive}
disabled={disabled}
onChange={change}
/>
</PageHeader>
<WebhookInfo
data={data}
disabled={disabled}
@ -126,19 +126,20 @@ const WebhookDetailsPage: React.FC<WebhookDetailsPageProps> = ({
onSyncEventChange={handleSyncEventsSelect}
onAsyncEventChange={handleAsyncEventsSelect}
/>
<FormSpacer />
<WebhookSubscriptionQuery
query={query}
setQuery={setQuery}
data={data}
/>
</Box>
</Content>
<Savebar
disabled={disabled}
state={saveButtonBarState}
onCancel={() => navigate(backUrl)}
onSubmit={submit}
/>
</Container>
</DetailedContent>
);
}}
</Form>

View file

@ -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)}

View file

@ -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