Merge branch 'master' into fix/unassign_user

This commit is contained in:
Krzysztof Wolski 2020-08-20 14:13:48 +02:00 committed by GitHub
commit 6f3c8647f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 18442 additions and 8419 deletions

View file

@ -14,7 +14,7 @@ jobs:
- name: Set domain - name: Set domain
# Set test instance domain based on branch name slug # Set test instance domain based on branch name slug
run: | run: |
echo "::set-env name=domain::${{ env.GITHUB_HEAD_REF_SLUG }}.dashboard.saleor.rocks" echo "::set-env name=domain::${{ env.GITHUB_HEAD_REF_SLUG_URL }}.dashboard.saleor.rocks"
- name: Configure AWS credentials - name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1 uses: aws-actions/configure-aws-credentials@v1
@ -34,4 +34,4 @@ jobs:
with: with:
step: deactivate-env step: deactivate-env
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
env: ${{ env.GITHUB_HEAD_REF_SLUG }} env: ${{ env.GITHUB_HEAD_REF_SLUG_URL }}

View file

@ -22,7 +22,7 @@ jobs:
with: with:
step: start step: start
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
env: ${{ env.GITHUB_HEAD_REF_SLUG }} env: ${{ env.GITHUB_HEAD_REF_SLUG_URL }}
ref: ${{ github.head_ref }} ref: ${{ github.head_ref }}
- name: Cache node modules - name: Cache node modules
@ -63,7 +63,7 @@ jobs:
- name: Set domain - name: Set domain
# Set test instance domain based on branch name slug # Set test instance domain based on branch name slug
run: | run: |
echo "::set-env name=domain::${{ env.GITHUB_HEAD_REF_SLUG }}.dashboard.saleor.rocks" echo "::set-env name=domain::${{ env.GITHUB_HEAD_REF_SLUG_URL }}.dashboard.saleor.rocks"
- name: Configure AWS credentials - name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1 uses: aws-actions/configure-aws-credentials@v1

View file

@ -81,8 +81,8 @@ jobs:
env: env:
API_URI: https://pwa.demo.saleor.rocks/graphql/ API_URI: https://pwa.demo.saleor.rocks/graphql/
with: with:
start: | build: npm run build
npm start start: npx http-server -a localhost -p 9000 build/dashboard
wait-on: http://localhost:9000/ wait-on: http://localhost:9000/
wait-on-timeout: 120 wait-on-timeout: 120
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1

View file

@ -16,6 +16,16 @@ All notable, unreleased changes to this project will be documented in this file.
- Refactor authorization - #624 by @dominik-zeglen - Refactor authorization - #624 by @dominik-zeglen
- Fix minor bugs - #628 by @dominik-zeglen - Fix minor bugs - #628 by @dominik-zeglen
- Add navigator button - #635 by @dominik-zeglen - Add navigator button - #635 by @dominik-zeglen
- Update to newest schema - #638 by @dominik-zeglen
- Fix missing save button - #636 by @dominik-zeglen
- Fix user avatars - #639 by @dominik-zeglen
- Reset modal state after closing - #644 by @dominik-zeglen
- Fix incorrect messages - #643 by @dominik-zeglen
- Do not use devserver to run cypress tests - #650 by @dominik-zeglen
- Fix updating product that has no variants - #649 by @dominik-zeglen
- Update checkbox design - #651 by @dominik-zeglen
- Add warehouse choice - #646 by @dominik-zeglen
- Fix user management modal actions - #637 by @eaglesemanation
## 2.10.1 ## 2.10.1

View file

@ -0,0 +1 @@
<svg width="12" height="18" viewBox="0 0 12 18" fill="none" xmlns="http://www.w3.org/2000/svg" ><path d="M0 11.2324L6.99609 0L6.1875 6.78516H11.6895L4.79883 18L5.51953 11.2324H0Z" fill="currentColor" /></svg>

After

Width:  |  Height:  |  Size: 208 B

View file

@ -205,6 +205,62 @@
"context": "vat not included in order price", "context": "vat not included in order price",
"string": "does not apply" "string": "does not apply"
}, },
"productExportFieldCategory": {
"context": "product field",
"string": "Category"
},
"productExportFieldCollections": {
"context": "product field",
"string": "Collections"
},
"productExportFieldDescription": {
"context": "product field",
"string": "Description"
},
"productExportFieldName": {
"context": "product field",
"string": "Name"
},
"productExportFieldPrice": {
"context": "product field",
"string": "Cost Price"
},
"productExportFieldProductImages": {
"context": "product field",
"string": "Product Images"
},
"productExportFieldProductWeight": {
"context": "product field",
"string": "Export Product Weight"
},
"productExportFieldTaxes": {
"context": "product field",
"string": "Charge Taxes"
},
"productExportFieldType": {
"context": "product field",
"string": "Type"
},
"productExportFieldVariantImages": {
"context": "product field",
"string": "Variant Images"
},
"productExportFieldVariantPrice": {
"context": "product field",
"string": "Variant Price"
},
"productExportFieldVariantSku": {
"context": "product field",
"string": "Export Variant SKU"
},
"productExportFieldVariantWeight": {
"context": "product field",
"string": "Export Variant Weight"
},
"productExportFieldVisibility": {
"context": "product field",
"string": "Visibility"
},
"productStatusLabel": { "productStatusLabel": {
"context": "product", "context": "product",
"string": "Published" "string": "Published"
@ -557,9 +613,9 @@
"context": "apps content", "context": "apps content",
"string": "You dont have any installed apps in your dashboard" "string": "You dont have any installed apps in your dashboard"
}, },
"src_dot_apps_dot_components_dot_InstalledApps_dot_2008086393": { "src_dot_apps_dot_components_dot_InstalledApps_dot_3504763431": {
"context": "section header", "context": "section header",
"string": "Thirdparty Apps" "string": "Third-party Apps"
}, },
"src_dot_apps_dot_components_dot_Marketplace_dot_2932910073": { "src_dot_apps_dot_components_dot_Marketplace_dot_2932910073": {
"context": "section header", "context": "section header",
@ -1055,6 +1111,9 @@
"context": "dialog title", "context": "dialog title",
"string": "Delete products" "string": "Delete products"
}, },
"src_dot_categories_dot_views_dot_3296953393": {
"string": "Remember this will also unpin all products assigned to this category, making them unavailable in storefront."
},
"src_dot_categories_dot_views_dot_3488150607": { "src_dot_categories_dot_views_dot_3488150607": {
"string": "Remember this will also delete all products assigned to this category." "string": "Remember this will also delete all products assigned to this category."
}, },
@ -1512,6 +1571,9 @@
"src_dot_components_dot_MultiSelectField_dot_4205644805": { "src_dot_components_dot_MultiSelectField_dot_4205644805": {
"string": "No results found" "string": "No results found"
}, },
"src_dot_components_dot_NavigatorButton_dot_154826674": {
"string": "Navigator"
},
"src_dot_components_dot_Navigator_dot_1116468870": { "src_dot_components_dot_Navigator_dot_1116468870": {
"context": "navigator placeholder", "context": "navigator placeholder",
"string": "Order Number" "string": "Order Number"
@ -3922,34 +3984,22 @@
"context": "product name", "context": "product name",
"string": "Name" "string": "Name"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_1004476569": {
"context": "option",
"string": "Export stock for all warehouses"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_108342258": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_108342258": {
"context": "button", "context": "button",
"string": "Load More" "string": "Load More"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_1459686496": {
"context": "product field",
"string": "Visibility"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_1547327218": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_1547327218": {
"context": "there are more elements of list that are hidden", "context": "there are more elements of list that are hidden",
"string": "and {number} more" "string": "and {number} more"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_1755013298": {
"context": "product field",
"string": "Category"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_1890035856": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_1890035856": {
"context": "informations about product organization, header", "context": "informations about product organization, header",
"string": "Product Organization" "string": "Product Organization"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_1952810469": {
"context": "product field",
"string": "Type"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2051669917": {
"context": "product field",
"string": "Cost Price"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2119710854": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_2119710854": {
"context": "informations about product seo, header", "context": "informations about product seo, header",
"string": "SEO Information" "string": "SEO Information"
@ -3958,10 +4008,6 @@
"context": "export selected products to csv file", "context": "export selected products to csv file",
"string": "Selected products ({number})" "string": "Selected products ({number})"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_222873645": {
"context": "product field",
"string": "Collections"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2318723509": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_2318723509": {
"context": "export products to csv file, choice field label", "context": "export products to csv file, choice field label",
"string": "Export information for:" "string": "Export information for:"
@ -3970,6 +4016,10 @@
"context": "export all products to csv file", "context": "export all products to csv file",
"string": "All products ({number})" "string": "All products ({number})"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2474350154": {
"context": "list of warehouses",
"string": "Warehouses A to Z"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2659464408": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_2659464408": {
"context": "product export to csv file, header", "context": "product export to csv file, header",
"string": "Information exported" "string": "Information exported"
@ -3982,10 +4032,6 @@
"context": "export products to csv file, button", "context": "export products to csv file, button",
"string": "export products" "string": "export products"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_2949617129": {
"context": "product field",
"string": "Product Images"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3012202273": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_3012202273": {
"context": "export products to csv file, dialog header", "context": "export products to csv file, dialog header",
"string": "Export Information" "string": "Export Information"
@ -3994,14 +4040,6 @@
"context": "product export to csv file, header", "context": "product export to csv file, header",
"string": "Export Settings" "string": "Export Settings"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3374163063": {
"context": "product field",
"string": "Description"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3441755345": {
"context": "product field",
"string": "Charge Taxes"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3443345452": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_3443345452": {
"context": "selectt all options", "context": "selectt all options",
"string": "Select All" "string": "Select All"
@ -4010,13 +4048,12 @@
"context": "export products as spreadsheet", "context": "export products as spreadsheet",
"string": "Spreadsheet for Excel, Numbers etc." "string": "Spreadsheet for Excel, Numbers etc."
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3544554440": {
"context": "product field",
"string": "Variant Weight"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3599582104": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_3599582104": {
"string": "Search Atrtibuttes" "string": "Search Atrtibuttes"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3902059658": {
"string": "Export Product Stock Quantity to CSV"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_3919525499": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_3919525499": {
"context": "informations about product stock, header", "context": "informations about product stock, header",
"string": "Inventory Information" "string": "Inventory Information"
@ -4025,10 +4062,6 @@
"context": "export products as csv file", "context": "export products as csv file",
"string": "Plain CSV file" "string": "Plain CSV file"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_4160582036": {
"context": "product field",
"string": "Variant Price"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_472026385": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_472026385": {
"context": "select product informations to be exported", "context": "select product informations to be exported",
"string": "Information exported:" "string": "Information exported:"
@ -4037,14 +4070,6 @@
"context": "input helper text, search attributes", "context": "input helper text, search attributes",
"string": "Search by attribute name" "string": "Search by attribute name"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_636461959": {
"context": "product field",
"string": "Name"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_693960049": {
"context": "product field",
"string": "SKU"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_700651641": { "src_dot_products_dot_components_dot_ProductExportDialog_dot_700651641": {
"context": "export filtered products to csv file", "context": "export filtered products to csv file",
"string": "Current search ({number})" "string": "Current search ({number})"
@ -4053,14 +4078,6 @@
"context": "informations about product prices etc, header", "context": "informations about product prices etc, header",
"string": "Financial Information" "string": "Financial Information"
}, },
"src_dot_products_dot_components_dot_ProductExportDialog_dot_746695941": {
"context": "product field",
"string": "Weight"
},
"src_dot_products_dot_components_dot_ProductExportDialog_dot_897437458": {
"context": "product field",
"string": "Variant Images"
},
"src_dot_products_dot_components_dot_ProductImageNavigation_dot_3060635772": { "src_dot_products_dot_components_dot_ProductImageNavigation_dot_3060635772": {
"context": "section header", "context": "section header",
"string": "All Photos" "string": "All Photos"
@ -5731,6 +5748,10 @@
"context": "dialog header", "context": "dialog header",
"string": "Delete Webhook" "string": "Delete Webhook"
}, },
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_1087314240": {
"context": "event",
"string": "Checkout updated"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_1368317066": { "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_1368317066": {
"context": "event", "context": "event",
"string": "Invoice deleted" "string": "Invoice deleted"
@ -5743,6 +5764,10 @@
"context": "event", "context": "event",
"string": "Order updated" "string": "Order updated"
}, },
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2240725235": {
"context": "event",
"string": "Checkout created"
},
"src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2454751033": { "src_dot_webhooks_dot_components_dot_WebhookEvents_dot_2454751033": {
"context": "event", "context": "event",
"string": "All events" "string": "All events"

File diff suppressed because it is too large Load diff

View file

@ -47,7 +47,7 @@ const InstalledApps: React.FC<InstalledAppsProps> = ({
header={ header={
<CardTitle <CardTitle
title={intl.formatMessage({ title={intl.formatMessage({
defaultMessage: "Thirdparty Apps", defaultMessage: "Third-party Apps",
description: "section header" description: "section header"
})} })}
/> />

View file

@ -17,7 +17,7 @@ import {
AttributeListUrlQueryParams AttributeListUrlQueryParams
} from "../../urls"; } from "../../urls";
export const PRODUCT_FILTERS_KEY = "productFilters"; export const ATTRIBUTE_FILTERS_KEY = "attributeFilters";
export function getFilterOpts( export function getFilterOpts(
params: AttributeListUrlFilters params: AttributeListUrlFilters
@ -130,7 +130,7 @@ export const {
deleteFilterTab, deleteFilterTab,
getFilterTabs, getFilterTabs,
saveFilterTab saveFilterTab
} = createFilterTabUtils<AttributeListUrlFilters>(PRODUCT_FILTERS_KEY); } = createFilterTabUtils<AttributeListUrlFilters>(ATTRIBUTE_FILTERS_KEY);
export const { areFiltersApplied, getActiveFilters } = createFilterUtils< export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
AttributeListUrlQueryParams, AttributeListUrlQueryParams,

View file

@ -8,7 +8,14 @@ export enum JWTError {
} }
export function isJwtError(error: GraphQLError): boolean { export function isJwtError(error: GraphQLError): boolean {
return !!findValueInEnum(error.extensions.exception.code, JWTError); let jwtError: boolean;
try {
jwtError = !!findValueInEnum(error.extensions.exception.code, JWTError);
} catch (e) {
jwtError = false;
}
return jwtError;
} }
export function isTokenExpired(error: GraphQLError): boolean { export function isTokenExpired(error: GraphQLError): boolean {

View file

@ -285,7 +285,7 @@ export const CategoryDetails: React.FC<CategoryDetailsProps> = ({
/> />
</DialogContentText> </DialogContentText>
<DialogContentText> <DialogContentText>
<FormattedMessage defaultMessage="Remember this will also delete all products assigned to this category." /> <FormattedMessage defaultMessage="Remember this will also unpin all products assigned to this category, making them unavailable in storefront." />
</DialogContentText> </DialogContentText>
</ActionDialog> </ActionDialog>
<ActionDialog <ActionDialog

View file

@ -28,6 +28,8 @@ import useRouter from "use-react-router";
import Container from "../Container"; import Container from "../Container";
import ErrorPage from "../ErrorPage"; import ErrorPage from "../ErrorPage";
import Navigator from "../Navigator";
import NavigatorButton from "../NavigatorButton/NavigatorButton";
import AppActionContext from "./AppActionContext"; import AppActionContext from "./AppActionContext";
import AppHeaderContext from "./AppHeaderContext"; import AppHeaderContext from "./AppHeaderContext";
import { appLoaderHeight, drawerWidth, drawerWidthExpanded } from "./consts"; import { appLoaderHeight, drawerWidth, drawerWidthExpanded } from "./consts";
@ -310,6 +312,7 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
const intl = useIntl(); const intl = useIntl();
const [appState, dispatchAppState] = useAppState(); const [appState, dispatchAppState] = useAppState();
const { location } = useRouter(); const { location } = useRouter();
const [isNavigatorVisible, setNavigatorVisibility] = React.useState(false);
const menuStructure = createMenuStructure(intl); const menuStructure = createMenuStructure(intl);
const configurationMenu = createConfigurationMenu(intl); const configurationMenu = createConfigurationMenu(intl);
@ -356,170 +359,184 @@ const AppLayout: React.FC<AppLayoutProps> = ({ children }) => {
}; };
return ( return (
<AppHeaderContext.Provider value={appHeaderAnchor}> <>
<AppActionContext.Provider value={appActionAnchor}> <Navigator
<div className={classes.root}> visible={isNavigatorVisible}
<div className={classes.sideBar}> setVisibility={setNavigatorVisibility}
<ResponsiveDrawer />
onClose={() => setDrawerState(false)} <AppHeaderContext.Provider value={appHeaderAnchor}>
open={isDrawerOpened} <AppActionContext.Provider value={appActionAnchor}>
small={!isMenuSmall} <div className={classes.root}>
> <div className={classes.sideBar}>
<div <ResponsiveDrawer
className={classNames(classes.logo, { onClose={() => setDrawerState(false)}
[classes.logoSmall]: isMenuSmall, open={isDrawerOpened}
[classes.logoDark]: isDark small={!isMenuSmall}
})}
> >
<SVG src={isMenuSmall ? saleorDarkLogoSmall : saleorDarkLogo} />
</div>
<Hidden smDown>
<div <div
className={classNames(classes.isMenuSmall, { className={classNames(classes.logo, {
[classes.isMenuSmallHide]: isMenuSmall, [classes.logoSmall]: isMenuSmall,
[classes.isMenuSmallDark]: isDark [classes.logoDark]: isDark
})} })}
onClick={handleIsMenuSmall}
> >
<SVG src={menuArrowIcon} /> <SVG
src={isMenuSmall ? saleorDarkLogoSmall : saleorDarkLogo}
/>
</div> </div>
</Hidden> <Hidden smDown>
<MenuList <div
className={isMenuSmall ? classes.menuSmall : classes.menu} className={classNames(classes.isMenuSmall, {
menuItems={menuStructure} [classes.isMenuSmallHide]: isMenuSmall,
isMenuSmall={!isMenuSmall} [classes.isMenuSmallDark]: isDark
location={location.pathname} })}
user={user} onClick={handleIsMenuSmall}
renderConfigure={renderConfigure} >
onMenuItemClick={handleMenuItemClick} <SVG src={menuArrowIcon} />
/> </div>
</ResponsiveDrawer> </Hidden>
</div> <MenuList
<div className={isMenuSmall ? classes.menuSmall : classes.menu}
className={classNames(classes.content, { menuItems={menuStructure}
[classes.contentToggle]: isMenuSmall isMenuSmall={!isMenuSmall}
})} location={location.pathname}
> user={user}
{appState.loading ? ( renderConfigure={renderConfigure}
<LinearProgress className={classes.appLoader} color="primary" /> onMenuItemClick={handleMenuItemClick}
) : ( />
<div className={classes.appLoaderPlaceholder} /> </ResponsiveDrawer>
)} </div>
<div className={classes.viewContainer}> <div
<div> className={classNames(classes.content, {
<Container> [classes.contentToggle]: isMenuSmall
<div className={classes.header}> })}
<div >
className={classNames(classes.menuIcon, { {appState.loading ? (
[classes.menuIconOpen]: isDrawerOpened, <LinearProgress className={classes.appLoader} color="primary" />
[classes.menuIconDark]: isDark ) : (
})} <div className={classes.appLoaderPlaceholder} />
onClick={() => setDrawerState(!isDrawerOpened)} )}
> <div className={classes.viewContainer}>
<span /> <div>
<span /> <Container>
<span /> <div className={classes.header}>
<span /> <div
</div> className={classNames(classes.menuIcon, {
<div ref={appHeaderAnchor} /> [classes.menuIconOpen]: isDrawerOpened,
<div className={classes.spacer} /> [classes.menuIconDark]: isDark
<div className={classes.userBar}> })}
<ThemeSwitch onClick={() => setDrawerState(!isDrawerOpened)}
className={classes.darkThemeSwitch} >
checked={isDark} <span />
onClick={toggleTheme} <span />
/> <span />
<div className={classes.userMenuContainer} ref={anchor}> <span />
<Chip </div>
avatar={ <div ref={appHeaderAnchor} />
user.avatar && ( <div className={classes.spacer} />
<Avatar alt="user" src={user.avatar.url} /> <div className={classes.userBar}>
) <ThemeSwitch
} className={classes.darkThemeSwitch}
classes={{ checked={isDark}
avatar: classes.avatar onClick={toggleTheme}
}}
className={classes.userChip}
label={
<>
{user.email}
<ArrowDropdown
className={classNames(classes.arrow, {
[classes.rotate]: isMenuOpened
})}
/>
</>
}
onClick={() => setMenuState(!isMenuOpened)}
data-test="userMenu"
/> />
<Popper <NavigatorButton
className={classes.popover} isMac={navigator.platform
open={isMenuOpened} .toLowerCase()
anchorEl={anchor.current} .includes("mac")}
transition onClick={() => setNavigatorVisibility(true)}
placement="bottom-end" />
> <div className={classes.userMenuContainer} ref={anchor}>
{({ TransitionProps, placement }) => ( <Chip
<Grow avatar={
{...TransitionProps} user.avatar && (
style={{ <Avatar alt="user" src={user.avatar.url} />
transformOrigin: )
placement === "bottom" }
? "right top" classes={{
: "right bottom" avatar: classes.avatar
}} }}
> className={classes.userChip}
<Paper> label={
<ClickAwayListener <>
onClickAway={() => setMenuState(false)} {user.email}
mouseEvent="onClick" <ArrowDropdown
> className={classNames(classes.arrow, {
<Menu> [classes.rotate]: isMenuOpened
<MenuItem })}
className={classes.userMenuItem} />
onClick={handleViewerProfile} </>
data-test="accountSettingsButton" }
> onClick={() => setMenuState(!isMenuOpened)}
<FormattedMessage data-test="userMenu"
defaultMessage="Account Settings" />
description="button" <Popper
/> className={classes.popover}
</MenuItem> open={isMenuOpened}
<MenuItem anchorEl={anchor.current}
className={classes.userMenuItem} transition
onClick={handleLogout} placement="bottom-end"
data-test="logOutButton" >
> {({ TransitionProps, placement }) => (
<FormattedMessage <Grow
defaultMessage="Log out" {...TransitionProps}
description="button" style={{
/> transformOrigin:
</MenuItem> placement === "bottom"
</Menu> ? "right top"
</ClickAwayListener> : "right bottom"
</Paper> }}
</Grow> >
)} <Paper>
</Popper> <ClickAwayListener
onClickAway={() => setMenuState(false)}
mouseEvent="onClick"
>
<Menu>
<MenuItem
className={classes.userMenuItem}
onClick={handleViewerProfile}
data-test="accountSettingsButton"
>
<FormattedMessage
defaultMessage="Account Settings"
description="button"
/>
</MenuItem>
<MenuItem
className={classes.userMenuItem}
onClick={handleLogout}
data-test="logOutButton"
>
<FormattedMessage
defaultMessage="Log out"
description="button"
/>
</MenuItem>
</Menu>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</div>
</div> </div>
</div> </div>
</div> </Container>
</Container> </div>
<main className={classes.view}>
{appState.error
? appState.error === "unhandled" && (
<ErrorPage onBack={handleErrorBack} />
)
: children}
</main>
</div> </div>
<main className={classes.view}> <div className={classes.appAction} ref={appActionAnchor} />
{appState.error
? appState.error === "unhandled" && (
<ErrorPage onBack={handleErrorBack} />
)
: children}
</main>
</div> </div>
<div className={classes.appAction} ref={appActionAnchor} />
</div> </div>
</div> </AppActionContext.Provider>
</AppActionContext.Provider> </AppHeaderContext.Provider>
</AppHeaderContext.Provider> </>
); );
}; };

View file

@ -1,136 +1,25 @@
import ButtonBase from "@material-ui/core/ButtonBase"; import MuiCheckbox, {
import { CheckboxProps as MuiCheckboxProps } from "@material-ui/core/Checkbox"; CheckboxProps as MuiCheckboxProps
import { makeStyles } from "@material-ui/core/styles"; } from "@material-ui/core/Checkbox";
import { fade } from "@material-ui/core/styles/colorManipulator";
import classNames from "classnames";
import React from "react"; import React from "react";
export type CheckboxProps = Omit< export type CheckboxProps = Omit<
MuiCheckboxProps, MuiCheckboxProps,
| "checkedIcon" "checkedIcon" | "color" | "icon" | "indeterminateIcon" | "classes" | "onClick"
| "color"
| "icon"
| "indeterminateIcon"
| "classes"
| "onChange"
| "onClick"
> & { > & {
disableClickPropagation?: boolean; disableClickPropagation?: boolean;
onChange?: (event: React.ChangeEvent<any>) => void;
}; };
const useStyles = makeStyles(
theme => ({
box: {
"&$checked": {
"&:before": {
background: theme.palette.primary.main,
color: theme.palette.background.paper,
content: '"\\2713"',
fontWeight: "bold",
textAlign: "center"
},
borderColor: theme.palette.primary.main
},
"&$disabled": {
borderColor: theme.palette.grey[200]
},
"&$indeterminate": {
"&:before": {
background: theme.palette.primary.main,
height: 2,
top: 5
},
borderColor: theme.palette.primary.main
},
"&:before": {
background: "rgba(0, 0, 0, 0)",
content: '""',
height: 14,
left: -1,
position: "absolute",
top: -1,
transition: theme.transitions.duration.short + "ms",
width: 14
},
WebkitAppearance: "none",
border: `1px solid ${theme.palette.action.active}`,
boxSizing: "border-box",
cursor: "pointer",
height: 14,
outline: "0",
position: "relative",
userSelect: "none",
width: 14
},
checked: {},
disabled: {},
indeterminate: {},
root: {
"&:hover": {
background: fade(theme.palette.primary.main, 0.1)
},
alignSelf: "start",
borderRadius: "100%",
cursor: "pointer",
display: "flex",
height: 30,
justifyContent: "center",
margin: "5px 9px",
width: 30
}
}),
{ name: "Checkbox" }
);
const Checkbox: React.FC<CheckboxProps> = props => { const Checkbox: React.FC<CheckboxProps> = props => {
const { const { disableClickPropagation, ...rest } = props;
checked,
className,
disabled,
disableClickPropagation,
indeterminate,
onChange,
value,
name,
...rest
} = props;
const classes = useStyles(props);
const inputRef = React.useRef<HTMLInputElement>(null);
const handleClick = React.useCallback(
disableClickPropagation
? event => {
event.stopPropagation();
inputRef.current.click();
}
: () => inputRef.current.click(),
[]
);
return ( return (
<ButtonBase <MuiCheckbox
{...rest} {...rest}
centerRipple onClick={
className={classNames(classes.root, className)} disableClickPropagation ? event => event.stopPropagation() : undefined
disabled={disabled} }
onClick={handleClick} />
>
<input
className={classNames(classes.box, {
[classes.checked]: checked,
[classes.disabled]: disabled,
[classes.indeterminate]: indeterminate
})}
disabled={disabled}
type="checkbox"
name={name}
value={checked !== undefined && checked.toString()}
ref={inputRef}
onChange={onChange}
/>
</ButtonBase>
); );
}; };
Checkbox.displayName = "Checkbox"; Checkbox.displayName = "Checkbox";

View file

@ -1,8 +1,7 @@
import Checkbox from "@material-ui/core/Checkbox";
import FormControlLabel from "@material-ui/core/FormControlLabel"; import FormControlLabel from "@material-ui/core/FormControlLabel";
import React from "react"; import React from "react";
import Checkbox from "./Checkbox";
interface ControlledCheckboxProps { interface ControlledCheckboxProps {
className?: string; className?: string;
name: string; name: string;
@ -27,7 +26,6 @@ export const ControlledCheckbox: React.FC<ControlledCheckboxProps> = ({
checked={checked} checked={checked}
disabled={disabled} disabled={disabled}
name={name} name={name}
disableClickPropagation
onChange={() => onChange({ target: { name, value: !checked } })} onChange={() => onChange({ target: { name, value: !checked } })}
/> />
} }

View file

@ -60,8 +60,12 @@ const useStyles = makeStyles(
} }
); );
const Navigator: React.FC = () => { export interface NavigatorProps {
const [visible, setVisible] = React.useState(false); visible: boolean;
setVisibility: (state: boolean) => void;
}
const Navigator: React.FC<NavigatorProps> = ({ visible, setVisibility }) => {
const input = React.useRef(null); const input = React.useRef(null);
const [query, mode, change, actions] = useQuickSearch(visible, input); const [query, mode, change, actions] = useQuickSearch(visible, input);
const intl = useIntl(); const intl = useIntl();
@ -76,7 +80,7 @@ const Navigator: React.FC = () => {
React.useEffect(() => { React.useEffect(() => {
hotkeys(navigatorHotkey, event => { hotkeys(navigatorHotkey, event => {
event.preventDefault(); event.preventDefault();
setVisible(!visible); setVisibility(!visible);
}); });
if (cmp(APP_VERSION, "2.1.0") !== 1 && !notifiedAboutNavigator) { if (cmp(APP_VERSION, "2.1.0") !== 1 && !notifiedAboutNavigator) {
@ -110,7 +114,7 @@ const Navigator: React.FC = () => {
<Modal <Modal
className={classes.modal} className={classes.modal}
open={visible} open={visible}
onClose={() => setVisible(false)} onClose={() => setVisibility(false)}
> >
<Fade appear in={visible} timeout={theme.transitions.duration.short}> <Fade appear in={visible} timeout={theme.transitions.duration.short}>
<div className={classes.root}> <div className={classes.root}>
@ -122,7 +126,7 @@ const Navigator: React.FC = () => {
onSelect={(item: QuickSearchAction) => { onSelect={(item: QuickSearchAction) => {
const shouldRemainVisible = item.onClick(); const shouldRemainVisible = item.onClick();
if (!shouldRemainVisible) { if (!shouldRemainVisible) {
setVisible(false); setVisibility(false);
} }
}} }}
onInputValueChange={value => onInputValueChange={value =>

View file

@ -0,0 +1,12 @@
import CardDecorator from "@saleor/storybook/CardDecorator";
import Decorator from "@saleor/storybook/Decorator";
import { storiesOf } from "@storybook/react";
import React from "react";
import NavigatorButton from "./NavigatorButton";
storiesOf("Generics / NavigatorButton", module)
.addDecorator(Decorator)
.addDecorator(CardDecorator)
.add("mac", () => <NavigatorButton isMac={true} />)
.add("other", () => <NavigatorButton isMac={false} />);

View file

@ -0,0 +1,153 @@
import navigatorIcon from "@assets/images/navigator.svg";
import Grow from "@material-ui/core/Grow";
import IconButton, { IconButtonProps } from "@material-ui/core/IconButton";
import Paper from "@material-ui/core/Paper";
import Popper from "@material-ui/core/Popper";
import makeStyles from "@material-ui/core/styles/makeStyles";
import classNames from "classnames";
import React from "react";
import ReactSVG from "react-inlinesvg";
import { FormattedMessage } from "react-intl";
const useStyles = makeStyles(
theme => {
const triangle = (color: string, width: number) => ({
borderBottom: `${width}px solid ${color}`,
borderLeft: `${width}px solid transparent`,
borderRight: `${width}px solid transparent`,
height: 0,
width: 0
});
return {
keyTile: {
"&:first-child": {
marginLeft: theme.spacing()
},
alignItems: "center",
background: theme.palette.background.default,
borderRadius: 8,
display: "inline-flex",
height: 32,
justifyContent: "center",
marginLeft: theme.spacing(0.5),
padding: theme.spacing(),
width: 32
},
keyTileLabel: {
verticalAlign: "middle"
},
paper: {
"&:after": {
...triangle(theme.palette.background.paper, 7),
content: "''",
left: theme.spacing(2) + 2,
position: "absolute",
top: -theme.spacing() + 1
},
"&:before": {
...triangle(theme.palette.divider, 8),
content: "''",
left: theme.spacing(2) + 1,
position: "absolute",
top: -theme.spacing()
},
border: `1px solid ${theme.palette.divider}`,
borderRadius: 6,
marginTop: theme.spacing(2),
padding: theme.spacing(2),
position: "relative"
},
root: {
"& path": {
color: theme.palette.primary.main
},
"&:not(:hover)": {
backgroundColor: theme.palette.background.paper
},
border: `1px solid ${theme.palette.divider}`,
height: 40,
marginRight: theme.spacing(2),
width: 40
}
};
},
{
name: "NavigatorButton"
}
);
export interface NavigatorButtonProps extends IconButtonProps {
isMac: boolean;
}
const NavigatorButton: React.FC<NavigatorButtonProps> = ({
className,
isMac,
...props
}) => {
const classes = useStyles({});
const helperTimer = React.useRef(null);
const [helperVisibility, setHelperVisibility] = React.useState(false);
const anchor = React.useRef<HTMLButtonElement>();
const setHelper = () => {
helperTimer.current = setTimeout(() => setHelperVisibility(true), 2 * 1000);
};
const clearHelper = () => {
if (helperTimer.current) {
clearTimeout(helperTimer.current);
helperTimer.current = null;
}
setHelperVisibility(false);
};
return (
<>
<IconButton
className={classNames(className, classes.root)}
data-test="navigator"
onMouseEnter={setHelper}
onMouseLeave={clearHelper}
{...props}
ref={anchor}
>
<ReactSVG src={navigatorIcon} />
</IconButton>
<Popper
open={helperVisibility}
anchorEl={anchor.current}
transition
disablePortal
placement="bottom-start"
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === "bottom" ? "right top" : "right bottom"
}}
>
<Paper className={classes.paper} elevation={0}>
<FormattedMessage defaultMessage="Navigator" />
<div className={classes.keyTile}>
<span className={classes.keyTileLabel}>
{isMac ? "⌘" : "Ctrl"}
</span>
</div>
<div className={classes.keyTile}>
<span className={classes.keyTileLabel}>K</span>
</div>
</Paper>
</Grow>
)}
</Popper>
</>
);
};
NavigatorButton.displayName = "NavigatorButton";
export default NavigatorButton;

View file

@ -28,23 +28,6 @@ const useStyles = makeStyles(
cell: { cell: {
padding: 0 padding: 0
}, },
checkboxPartialSelect: {
"& input": {
"&:before": {
background: [theme.palette.background.paper, "!important"] as any,
border: `solid 1px ${theme.palette.primary.main}`,
content: "''"
},
background: theme.palette.background.paper
},
"&:after": {
background: theme.palette.primary.main,
content: "''",
height: 2,
position: "absolute",
width: 6
}
},
checkboxSelected: { checkboxSelected: {
backgroundColor: fade(theme.palette.primary.main, 0.05) backgroundColor: fade(theme.palette.primary.main, 0.05)
}, },
@ -113,10 +96,7 @@ const TableHead: React.FC<TableHeadProps> = props => {
})} })}
> >
<Checkbox <Checkbox
className={classNames({ indeterminate={items && items.length > selected && selected > 0}
[classes.checkboxPartialSelect]:
items && items.length > selected && selected > 0
})}
checked={selected === 0 ? false : true} checked={selected === 0 ? false : true}
disabled={disabled} disabled={disabled}
onChange={() => toggleAll(items, selected)} onChange={() => toggleAll(items, selected)}

View file

@ -117,4 +117,7 @@ export const {
export const { areFiltersApplied, getActiveFilters } = createFilterUtils< export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
SaleListUrlQueryParams, SaleListUrlQueryParams,
SaleListUrlFilters SaleListUrlFilters
>(SaleListUrlFiltersEnum); >({
...SaleListUrlFiltersEnum,
...SaleListUrlFiltersWithMultipleValues
});

View file

@ -149,4 +149,7 @@ export const {
export const { areFiltersApplied, getActiveFilters } = createFilterUtils< export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
VoucherListUrlQueryParams, VoucherListUrlQueryParams,
VoucherListUrlFilters VoucherListUrlFilters
>(VoucherListUrlFiltersEnum); >({
...VoucherListUrlFiltersEnum,
...VoucherListUrlFiltersWithMultipleValues
});

View file

@ -33,6 +33,9 @@ export const permissionGroupDetailsFragment = gql`
} }
users { users {
...StaffMemberFragment ...StaffMemberFragment
avatar(size: 48) {
url
}
} }
} }
`; `;

View file

@ -7,9 +7,6 @@ export const staffMemberFragment = gql`
firstName firstName
isActive isActive
lastName lastName
avatar {
url
}
} }
`; `;
export const staffMemberDetailsFragment = gql` export const staffMemberDetailsFragment = gql`
@ -25,5 +22,8 @@ export const staffMemberDetailsFragment = gql`
code code
name name
} }
avatar(size: 120) {
url
}
} }
`; `;

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeInputTypeEnum } from "./../../types/globalTypes"; import { AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: Product // GraphQL fragment: Product
@ -170,7 +170,7 @@ export interface Product_variants {
export interface Product_weight { export interface Product_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,6 +2,8 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: ProductTypeDetailsFragment // GraphQL fragment: ProductTypeDetailsFragment
// ==================================================== // ====================================================
@ -34,7 +36,7 @@ export interface ProductTypeDetailsFragment_variantAttributes {
export interface ProductTypeDetailsFragment_weight { export interface ProductTypeDetailsFragment_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,6 +2,8 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: ProductVariant // GraphQL fragment: ProductVariant
// ==================================================== // ====================================================
@ -105,7 +107,7 @@ export interface ProductVariant_stocks {
export interface ProductVariant_weight { export interface ProductVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ShippingMethodTypeEnum } from "./../../types/globalTypes"; import { WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: ShippingMethodFragment // GraphQL fragment: ShippingMethodFragment
@ -16,7 +16,7 @@ export interface ShippingMethodFragment_minimumOrderPrice {
export interface ShippingMethodFragment_minimumOrderWeight { export interface ShippingMethodFragment_minimumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -28,7 +28,7 @@ export interface ShippingMethodFragment_maximumOrderPrice {
export interface ShippingMethodFragment_maximumOrderWeight { export interface ShippingMethodFragment_maximumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ShippingMethodTypeEnum } from "./../../types/globalTypes"; import { WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: ShippingZoneDetailsFragment // GraphQL fragment: ShippingZoneDetailsFragment
@ -22,7 +22,7 @@ export interface ShippingZoneDetailsFragment_shippingMethods_minimumOrderPrice {
export interface ShippingZoneDetailsFragment_shippingMethods_minimumOrderWeight { export interface ShippingZoneDetailsFragment_shippingMethods_minimumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -34,7 +34,7 @@ export interface ShippingZoneDetailsFragment_shippingMethods_maximumOrderPrice {
export interface ShippingZoneDetailsFragment_shippingMethods_maximumOrderWeight { export interface ShippingZoneDetailsFragment_shippingMethods_maximumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -8,11 +8,6 @@ import { PermissionEnum } from "./../../types/globalTypes";
// GraphQL fragment: StaffMemberDetailsFragment // GraphQL fragment: StaffMemberDetailsFragment
// ==================================================== // ====================================================
export interface StaffMemberDetailsFragment_avatar {
__typename: "Image";
url: string;
}
export interface StaffMemberDetailsFragment_permissionGroups { export interface StaffMemberDetailsFragment_permissionGroups {
__typename: "Group"; __typename: "Group";
id: string; id: string;
@ -26,6 +21,11 @@ export interface StaffMemberDetailsFragment_userPermissions {
name: string; name: string;
} }
export interface StaffMemberDetailsFragment_avatar {
__typename: "Image";
url: string;
}
export interface StaffMemberDetailsFragment { export interface StaffMemberDetailsFragment {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -33,7 +33,7 @@ export interface StaffMemberDetailsFragment {
firstName: string; firstName: string;
isActive: boolean; isActive: boolean;
lastName: string; lastName: string;
avatar: StaffMemberDetailsFragment_avatar | null;
permissionGroups: (StaffMemberDetailsFragment_permissionGroups | null)[] | null; permissionGroups: (StaffMemberDetailsFragment_permissionGroups | null)[] | null;
userPermissions: (StaffMemberDetailsFragment_userPermissions | null)[] | null; userPermissions: (StaffMemberDetailsFragment_userPermissions | null)[] | null;
avatar: StaffMemberDetailsFragment_avatar | null;
} }

View file

@ -6,11 +6,6 @@
// GraphQL fragment: StaffMemberFragment // GraphQL fragment: StaffMemberFragment
// ==================================================== // ====================================================
export interface StaffMemberFragment_avatar {
__typename: "Image";
url: string;
}
export interface StaffMemberFragment { export interface StaffMemberFragment {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -18,5 +13,4 @@ export interface StaffMemberFragment {
firstName: string; firstName: string;
isActive: boolean; isActive: boolean;
lastName: string; lastName: string;
avatar: StaffMemberFragment_avatar | null;
} }

View file

@ -2,12 +2,14 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL fragment: WeightFragment // GraphQL fragment: WeightFragment
// ==================================================== // ====================================================
export interface WeightFragment { export interface WeightFragment {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

18
src/icons/Checkbox.tsx Normal file
View file

@ -0,0 +1,18 @@
import createSvgIcon from "@material-ui/icons/utils/createSvgIcon";
import React from "react";
const Checkbox = createSvgIcon(
<>
<rect
x="5"
y="5"
width="14"
height="14"
stroke="currentColor"
fill="none"
/>
</>,
"Checkbox"
);
export default Checkbox;

View file

@ -0,0 +1,17 @@
import createSvgIcon from "@material-ui/icons/utils/createSvgIcon";
import React from "react";
const CheckboxChecked = createSvgIcon(
<>
<rect x="5" y="5" width="14" height="14" fill="currentColor" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M 16.7527 9.33783 L 10.86618 15.7595 L 8 12.32006 L 8.76822 11.67988 L 10.90204 14.24046 L 16.0155 8.66211 L 16.7527 9.33783 Z"
fill="white"
/>
</>,
"CheckboxChecked"
);
export default CheckboxChecked;

View file

@ -0,0 +1,19 @@
import createSvgIcon from "@material-ui/icons/utils/createSvgIcon";
import React from "react";
const CheckboxIndeterminate = createSvgIcon(
<>
<rect
x="5"
y="5"
width="14"
height="14"
stroke="currentColor"
fill="none"
/>
<rect x="8" y="11" width="8" height="2" fill="currentColor" />
</>,
"CheckboxIndeterminate"
);
export default CheckboxIndeterminate;

View file

@ -1,4 +1,3 @@
import Navigator from "@saleor/components/Navigator";
import useAppState from "@saleor/hooks/useAppState"; import useAppState from "@saleor/hooks/useAppState";
import { defaultDataIdFromObject, InMemoryCache } from "apollo-cache-inmemory"; import { defaultDataIdFromObject, InMemoryCache } from "apollo-cache-inmemory";
import { ApolloClient } from "apollo-client"; import { ApolloClient } from "apollo-client";
@ -139,7 +138,6 @@ const Routes: React.FC = () => {
<WindowTitle title={intl.formatMessage(commonMessages.dashboard)} /> <WindowTitle title={intl.formatMessage(commonMessages.dashboard)} />
{isAuthenticated && !tokenAuthLoading && !tokenVerifyLoading ? ( {isAuthenticated && !tokenAuthLoading && !tokenVerifyLoading ? (
<AppLayout> <AppLayout>
<Navigator />
<ErrorBoundary <ErrorBoundary
onError={() => onError={() =>
dispatchAppState({ dispatchAppState({

View file

@ -306,10 +306,10 @@ export function createHref(url: string) {
interface AnyEvent { interface AnyEvent {
stopPropagation: () => void; stopPropagation: () => void;
} }
export function stopPropagation(cb: () => void) { export function stopPropagation(cb: (event?: AnyEvent) => void) {
return (event: AnyEvent) => { return (event: AnyEvent) => {
event.stopPropagation(); event.stopPropagation();
cb(); cb(event);
}; };
} }

View file

@ -52,8 +52,8 @@ const useStyles = makeStyles(
width: 47 width: 47
}, },
avatarDefault: { avatarDefault: {
"& p": { "& div": {
color: "#fff", color: theme.palette.primary.contrastText,
lineHeight: "47px" lineHeight: "47px"
}, },
background: theme.palette.primary.main, background: theme.palette.primary.main,

View file

@ -3,7 +3,7 @@ import {
SearchProductTypes_search_edges_node_productAttributes SearchProductTypes_search_edges_node_productAttributes
} from "@saleor/searches/types/SearchProductTypes"; } from "@saleor/searches/types/SearchProductTypes";
import { AttributeInputTypeEnum } from "../types/globalTypes"; import { AttributeInputTypeEnum, WeightUnitsEnum } from "../types/globalTypes";
import { ProductTypeDetails_productType } from "./types/ProductTypeDetails"; import { ProductTypeDetails_productType } from "./types/ProductTypeDetails";
import { ProductTypeList_productTypes_edges_node } from "./types/ProductTypeList"; import { ProductTypeList_productTypes_edges_node } from "./types/ProductTypeList";
@ -601,7 +601,7 @@ export const productType: ProductTypeDetails_productType = {
], ],
weight: { weight: {
__typename: "Weight", __typename: "Weight",
unit: "kg", unit: WeightUnitsEnum.KG,
value: 7.82 value: 7.82
} }
}; };

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeAssignInput } from "./../../types/globalTypes"; import { AttributeAssignInput, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: AssignAttribute // GraphQL mutation operation: AssignAttribute
@ -42,7 +42,7 @@ export interface AssignAttribute_attributeAssign_productType_variantAttributes {
export interface AssignAttribute_attributeAssign_productType_weight { export interface AssignAttribute_attributeAssign_productType_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ReorderInput, AttributeTypeEnum } from "./../../types/globalTypes"; import { ReorderInput, AttributeTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductTypeAttributeReorder // GraphQL mutation operation: ProductTypeAttributeReorder
@ -42,7 +42,7 @@ export interface ProductTypeAttributeReorder_productTypeReorderAttributes_produc
export interface ProductTypeAttributeReorder_productTypeReorderAttributes_productType_weight { export interface ProductTypeAttributeReorder_productTypeReorderAttributes_productType_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductTypeInput } from "./../../types/globalTypes"; import { ProductTypeInput, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductTypeCreate // GraphQL mutation operation: ProductTypeCreate
@ -42,7 +42,7 @@ export interface ProductTypeCreate_productTypeCreate_productType_variantAttribut
export interface ProductTypeCreate_productTypeCreate_productType_weight { export interface ProductTypeCreate_productTypeCreate_productType_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -36,7 +36,7 @@ export interface ProductTypeDetails_productType_variantAttributes {
export interface ProductTypeDetails_productType_weight { export interface ProductTypeDetails_productType_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductTypeInput } from "./../../types/globalTypes"; import { ProductTypeInput, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductTypeUpdate // GraphQL mutation operation: ProductTypeUpdate
@ -42,7 +42,7 @@ export interface ProductTypeUpdate_productTypeUpdate_productType_variantAttribut
export interface ProductTypeUpdate_productTypeUpdate_productType_weight { export interface ProductTypeUpdate_productTypeUpdate_productType_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,6 +2,8 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: UnassignAttribute // GraphQL mutation operation: UnassignAttribute
// ==================================================== // ====================================================
@ -40,7 +42,7 @@ export interface UnassignAttribute_attributeUnassign_productType_variantAttribut
export interface UnassignAttribute_attributeUnassign_productType_weight { export interface UnassignAttribute_attributeUnassign_productType_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -3,6 +3,7 @@ import {
ExportErrorCode, ExportErrorCode,
ExportProductsInput ExportProductsInput
} from "@saleor/types/globalTypes"; } from "@saleor/types/globalTypes";
import { warehouseList } from "@saleor/warehouses/fixtures";
import { storiesOf } from "@storybook/react"; import { storiesOf } from "@storybook/react";
import React from "react"; import React from "react";
@ -30,7 +31,8 @@ const props: ProductExportDialogProps = {
all: 100, all: 100,
filter: 32 filter: 32
}, },
selectedProducts: 18 selectedProducts: 18,
warehouses: warehouseList
}; };
storiesOf("Views / Products / Export / Export settings", module) storiesOf("Views / Products / Export / Export settings", module)

View file

@ -8,11 +8,11 @@ import ConfirmButton, {
ConfirmButtonTransitionState ConfirmButtonTransitionState
} from "@saleor/components/ConfirmButton"; } from "@saleor/components/ConfirmButton";
import makeCreatorSteps, { Step } from "@saleor/components/CreatorSteps"; import makeCreatorSteps, { Step } from "@saleor/components/CreatorSteps";
import Form from "@saleor/components/Form";
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
import { ExportErrorFragment } from "@saleor/fragments/types/ExportErrorFragment"; import { ExportErrorFragment } from "@saleor/fragments/types/ExportErrorFragment";
import { FormChange } from "@saleor/hooks/useForm"; import useForm, { FormChange } from "@saleor/hooks/useForm";
import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors"; import useModalDialogErrors from "@saleor/hooks/useModalDialogErrors";
import useModalDialogOpen from "@saleor/hooks/useModalDialogOpen";
import useWizard from "@saleor/hooks/useWizard"; import useWizard from "@saleor/hooks/useWizard";
import { buttonMessages } from "@saleor/intl"; import { buttonMessages } from "@saleor/intl";
import { SearchAttributes_search_edges_node } from "@saleor/searches/types/SearchAttributes"; import { SearchAttributes_search_edges_node } from "@saleor/searches/types/SearchAttributes";
@ -25,11 +25,13 @@ import {
import getExportErrorMessage from "@saleor/utils/errors/export"; import getExportErrorMessage from "@saleor/utils/errors/export";
import { toggle } from "@saleor/utils/lists"; import { toggle } from "@saleor/utils/lists";
import { mapNodeToChoice } from "@saleor/utils/maps"; import { mapNodeToChoice } from "@saleor/utils/maps";
import { WarehouseList_warehouses_edges_node } from "@saleor/warehouses/types/WarehouseList";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import ProductExportDialogInfo, { import ProductExportDialogInfo, {
attributeNamePrefix attributeNamePrefix,
warehouseNamePrefix
} from "./ProductExportDialogInfo"; } from "./ProductExportDialogInfo";
import ProductExportDialogSettings, { import ProductExportDialogSettings, {
ProductQuantity ProductQuantity
@ -64,7 +66,8 @@ function useSteps(): Array<Step<ProductExportStep>> {
const initialForm: ExportProductsInput = { const initialForm: ExportProductsInput = {
exportInfo: { exportInfo: {
attributes: [], attributes: [],
fields: [] fields: [],
warehouses: []
}, },
fileType: FileTypesEnum.CSV, fileType: FileTypesEnum.CSV,
scope: ExportScope.ALL scope: ExportScope.ALL
@ -78,6 +81,7 @@ export interface ProductExportDialogProps extends DialogProps, FetchMoreProps {
errors: ExportErrorFragment[]; errors: ExportErrorFragment[];
productQuantity: ProductQuantity; productQuantity: ProductQuantity;
selectedProducts: number; selectedProducts: number;
warehouses: WarehouseList_warehouses_edges_node[];
onFetch: (query: string) => void; onFetch: (query: string) => void;
onSubmit: (data: ExportProductsInput) => void; onSubmit: (data: ExportProductsInput) => void;
} }
@ -91,6 +95,7 @@ const ProductExportDialog: React.FC<ProductExportDialogProps> = ({
onSubmit, onSubmit,
open, open,
selectedProducts, selectedProducts,
warehouses,
...fetchMoreProps ...fetchMoreProps
}) => { }) => {
const [step, { next, prev, set: setStep }] = useWizard( const [step, { next, prev, set: setStep }] = useWizard(
@ -104,121 +109,153 @@ const ProductExportDialog: React.FC<ProductExportDialogProps> = ({
const [selectedAttributes, setSelectedAttributes] = React.useState< const [selectedAttributes, setSelectedAttributes] = React.useState<
MultiAutocompleteChoiceType[] MultiAutocompleteChoiceType[]
>([]); >([]);
const { change, data, reset, submit } = useForm(initialForm, onSubmit);
useModalDialogOpen(open, {
onClose: () => {
reset();
setStep(ProductExportStep.INFO);
}
});
const attributeChoices = mapNodeToChoice(attributes); const attributeChoices = mapNodeToChoice(attributes);
const warehouseChoices = mapNodeToChoice(warehouses);
const handleAttributeSelect: FormChange = event => {
const id = event.target.name.substr(attributeNamePrefix.length);
change({
target: {
name: "exportInfo",
value: {
...data.exportInfo,
attributes: toggle(id, data.exportInfo.attributes, (a, b) => a === b)
}
}
});
const choice = attributeChoices.find(choice => choice.value === id);
setSelectedAttributes(
toggle(choice, selectedAttributes, (a, b) => a.value === b.value)
);
};
const handleWarehouseSelect: FormChange = event =>
change({
target: {
name: "exportInfo",
value: {
...data.exportInfo,
warehouses: toggle(
event.target.name.substr(warehouseNamePrefix.length),
data.exportInfo.warehouses,
(a, b) => a === b
)
}
}
});
const handleToggleAllWarehouses: FormChange = () =>
change({
target: {
name: "exportInfo",
value: {
...data.exportInfo,
warehouses:
data.exportInfo.warehouses.length === warehouses.length
? []
: warehouses.map(warehouse => warehouse.id)
}
}
});
return ( return (
<Dialog onClose={onClose} open={open} maxWidth="sm" fullWidth> <Dialog onClose={onClose} open={open} maxWidth="sm" fullWidth>
<Form initial={initialForm} onSubmit={onSubmit}> <>
{({ change, data }) => { <DialogTitle>
const handleAttributeSelect: FormChange = event => { <FormattedMessage
const id = event.target.name.substr(attributeNamePrefix.length); defaultMessage="Export Information"
description="export products to csv file, dialog header"
/>
</DialogTitle>
<DialogContent>
<ProductExportSteps
currentStep={step}
steps={steps}
onStepClick={setStep}
/>
{step === ProductExportStep.INFO && (
<ProductExportDialogInfo
attributes={attributeChoices}
data={data}
selectedAttributes={selectedAttributes}
onAttrtibuteSelect={handleAttributeSelect}
onWarehouseSelect={handleWarehouseSelect}
onChange={change}
warehouses={warehouseChoices}
onSelectAllWarehouses={handleToggleAllWarehouses}
{...fetchMoreProps}
/>
)}
{step === ProductExportStep.SETTINGS && (
<ProductExportDialogSettings
data={data}
errors={dialogErrors}
productQuantity={productQuantity}
selectedProducts={selectedProducts}
onChange={change}
/>
)}
</DialogContent>
change({ {notFormErrors.length > 0 && (
target: { <DialogContent>
name: "exportInfo", {notFormErrors.map(err => (
value: { <Typography color="error" key={err.field + err.code}>
...data.exportInfo, {getExportErrorMessage(err, intl)}
attributes: toggle( </Typography>
id, ))}
data.exportInfo.attributes, </DialogContent>
(a, b) => a === b )}
)
}
}
});
const choice = attributeChoices.find(choice => choice.value === id); <DialogActions>
{step === ProductExportStep.INFO && (
setSelectedAttributes( <Button onClick={onClose} data-test="cancel">
toggle(choice, selectedAttributes, (a, b) => a.value === b.value) <FormattedMessage {...buttonMessages.cancel} />
); </Button>
}; )}
{step === ProductExportStep.SETTINGS && (
return ( <Button onClick={prev} data-test="back">
<> <FormattedMessage {...buttonMessages.back} />
<DialogTitle> </Button>
<FormattedMessage )}
defaultMessage="Export Information" {step === ProductExportStep.INFO && (
description="export products to csv file, dialog header" <Button
/> color="primary"
</DialogTitle> variant="contained"
<DialogContent> onClick={next}
<ProductExportSteps data-test="next"
currentStep={step} >
steps={steps} <FormattedMessage {...buttonMessages.nextStep} />
onStepClick={setStep} </Button>
/> )}
{step === ProductExportStep.INFO && ( {step === ProductExportStep.SETTINGS && (
<ProductExportDialogInfo <ConfirmButton
attributes={attributeChoices} transitionState={confirmButtonState}
data={data} variant="contained"
selectedAttributes={selectedAttributes} type="submit"
onAttrtibuteSelect={handleAttributeSelect} data-test="submit"
onChange={change} onClick={submit}
{...fetchMoreProps} >
/> <FormattedMessage
)} defaultMessage="export products"
{step === ProductExportStep.SETTINGS && ( description="export products to csv file, button"
<ProductExportDialogSettings />
data={data} </ConfirmButton>
errors={dialogErrors} )}
productQuantity={productQuantity} </DialogActions>
selectedProducts={selectedProducts} </>
onChange={change}
/>
)}
</DialogContent>
{notFormErrors.length > 0 && (
<DialogContent>
{notFormErrors.map(err => (
<Typography color="error" key={err.field + err.code}>
{getExportErrorMessage(err, intl)}
</Typography>
))}
</DialogContent>
)}
<DialogActions>
{step === ProductExportStep.INFO && (
<Button onClick={onClose} data-test="cancel">
<FormattedMessage {...buttonMessages.cancel} />
</Button>
)}
{step === ProductExportStep.SETTINGS && (
<Button onClick={prev} data-test="back">
<FormattedMessage {...buttonMessages.back} />
</Button>
)}
{step === ProductExportStep.INFO && (
<Button
color="primary"
variant="contained"
onClick={next}
data-test="next"
>
<FormattedMessage {...buttonMessages.nextStep} />
</Button>
)}
{step === ProductExportStep.SETTINGS && (
<ConfirmButton
transitionState={confirmButtonState}
variant="contained"
type="submit"
data-test="submit"
>
<FormattedMessage
defaultMessage="export products"
description="export products to csv file, button"
/>
</ConfirmButton>
)}
</DialogActions>
</>
);
}}
</Form>
</Dialog> </Dialog>
); );
}; };

View file

@ -1,15 +1,15 @@
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import Checkbox from "@material-ui/core/Checkbox";
import CircularProgress from "@material-ui/core/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress";
import FormControlLabel from "@material-ui/core/FormControlLabel"; import FormControlLabel from "@material-ui/core/FormControlLabel";
import makeStyles from "@material-ui/core/styles/makeStyles"; import makeStyles from "@material-ui/core/styles/makeStyles";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Accordion, { AccordionProps } from "@saleor/components/Accordion"; import Accordion, { AccordionProps } from "@saleor/components/Accordion";
import Checkbox from "@saleor/components/Checkbox";
import Chip from "@saleor/components/Chip"; import Chip from "@saleor/components/Chip";
import Hr from "@saleor/components/Hr"; import Hr from "@saleor/components/Hr";
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField"; import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
import { ChangeEvent } from "@saleor/hooks/useForm"; import { ChangeEvent, FormChange } from "@saleor/hooks/useForm";
import useSearchQuery from "@saleor/hooks/useSearchQuery"; import useSearchQuery from "@saleor/hooks/useSearchQuery";
import { sectionNames } from "@saleor/intl"; import { sectionNames } from "@saleor/intl";
import { FetchMoreProps } from "@saleor/types"; import { FetchMoreProps } from "@saleor/types";
@ -22,9 +22,18 @@ import React from "react";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import useProductExportFieldMessages from "./messages";
export const attributeNamePrefix = "attribute-"; export const attributeNamePrefix = "attribute-";
export const warehouseNamePrefix = "warehouse-";
const maxChips = 4; const maxChips = 4;
const inventoryFields = [
ProductFieldEnum.PRODUCT_WEIGHT,
ProductFieldEnum.VARIANT_SKU,
ProductFieldEnum.VARIANT_WEIGHT
];
const useStyles = makeStyles( const useStyles = makeStyles(
theme => ({ theme => ({
accordion: { accordion: {
@ -45,12 +54,23 @@ const useStyles = makeStyles(
marginBottom: theme.spacing(3), marginBottom: theme.spacing(3),
marginTop: theme.spacing(3) marginTop: theme.spacing(3)
}, },
hrWarehouses: {
marginBottom: theme.spacing(3),
marginTop: theme.spacing(1)
},
label: { label: {
"&&": { "&&": {
overflow: "visible" overflow: "visible"
}, },
"&:first-of-type": {
paddingTop: 0
},
"&:not(:last-of-type)": {
borderBottom: `1px solid ${theme.palette.divider}`
},
justifyContent: "space-between", justifyContent: "space-between",
margin: theme.spacing(0), margin: theme.spacing(0),
padding: theme.spacing(1, 0),
width: "100%" width: "100%"
}, },
loadMoreContainer: { loadMoreContainer: {
@ -62,8 +82,14 @@ const useStyles = makeStyles(
display: "inline-block", display: "inline-block",
marginBottom: theme.spacing() marginBottom: theme.spacing()
}, },
optionLabel: {
marginLeft: 0
},
quickPeekContainer: { quickPeekContainer: {
marginBottom: theme.spacing(-1) marginBottom: theme.spacing(-1)
},
warehousesLabel: {
marginBottom: theme.spacing(2)
} }
}), }),
{ {
@ -80,12 +106,14 @@ const Option: React.FC<{
return ( return (
<FormControlLabel <FormControlLabel
classes={{
label: classes.optionLabel
}}
color="primary" color="primary"
control={ control={
<Checkbox <Checkbox
className={classes.checkbox} className={classes.checkbox}
checked={checked} checked={checked}
color="primary"
name={name} name={name}
onChange={onChange} onChange={onChange}
/> />
@ -104,66 +132,7 @@ const FieldAccordion: React.FC<AccordionProps & {
onToggleAll: (field: ProductFieldEnum[], setTo: boolean) => void; onToggleAll: (field: ProductFieldEnum[], setTo: boolean) => void;
}> = ({ data, fields, onChange, onToggleAll, ...props }) => { }> = ({ data, fields, onChange, onToggleAll, ...props }) => {
const classes = useStyles({}); const classes = useStyles({});
const intl = useIntl(); const getFieldLabel = useProductExportFieldMessages();
const fieldNames: Record<ProductFieldEnum, string> = {
[ProductFieldEnum.CATEGORY]: intl.formatMessage({
defaultMessage: "Category",
description: "product field"
}),
[ProductFieldEnum.CHARGE_TAXES]: intl.formatMessage({
defaultMessage: "Charge Taxes",
description: "product field"
}),
[ProductFieldEnum.COLLECTIONS]: intl.formatMessage({
defaultMessage: "Collections",
description: "product field"
}),
[ProductFieldEnum.COST_PRICE]: intl.formatMessage({
defaultMessage: "Cost Price",
description: "product field"
}),
[ProductFieldEnum.DESCRIPTION]: intl.formatMessage({
defaultMessage: "Description",
description: "product field"
}),
[ProductFieldEnum.NAME]: intl.formatMessage({
defaultMessage: "Name",
description: "product field"
}),
[ProductFieldEnum.PRODUCT_IMAGES]: intl.formatMessage({
defaultMessage: "Product Images",
description: "product field"
}),
[ProductFieldEnum.PRODUCT_TYPE]: intl.formatMessage({
defaultMessage: "Type",
description: "product field"
}),
[ProductFieldEnum.PRODUCT_WEIGHT]: intl.formatMessage({
defaultMessage: "Weight",
description: "product field"
}),
[ProductFieldEnum.VARIANT_IMAGES]: intl.formatMessage({
defaultMessage: "Variant Images",
description: "product field"
}),
[ProductFieldEnum.VARIANT_PRICE]: intl.formatMessage({
defaultMessage: "Variant Price",
description: "product field"
}),
[ProductFieldEnum.VARIANT_SKU]: intl.formatMessage({
defaultMessage: "SKU",
description: "product field"
}),
[ProductFieldEnum.VARIANT_WEIGHT]: intl.formatMessage({
defaultMessage: "Variant Weight",
description: "product field"
}),
[ProductFieldEnum.VISIBLE]: intl.formatMessage({
defaultMessage: "Visibility",
description: "product field"
})
};
const selectedAll = fields.every(field => const selectedAll = fields.every(field =>
data.exportInfo.fields.includes(field) data.exportInfo.fields.includes(field)
@ -181,7 +150,7 @@ const FieldAccordion: React.FC<AccordionProps & {
{selectedFields.slice(0, maxChips).map(field => ( {selectedFields.slice(0, maxChips).map(field => (
<Chip <Chip
className={classes.chip} className={classes.chip}
label={fieldNames[field]} label={getFieldLabel(field)}
onClose={() => onClose={() =>
onChange({ onChange({
target: { target: {
@ -190,6 +159,7 @@ const FieldAccordion: React.FC<AccordionProps & {
} }
}) })
} }
key={field}
/> />
))} ))}
{selectedFields.length > maxChips && ( {selectedFields.length > maxChips && (
@ -225,7 +195,7 @@ const FieldAccordion: React.FC<AccordionProps & {
onChange={onChange} onChange={onChange}
key={field} key={field}
> >
{fieldNames[field]} {getFieldLabel(field)}
</Option> </Option>
))} ))}
</Accordion> </Accordion>
@ -234,11 +204,14 @@ const FieldAccordion: React.FC<AccordionProps & {
export interface ProductExportDialogInfoProps extends FetchMoreProps { export interface ProductExportDialogInfoProps extends FetchMoreProps {
attributes: MultiAutocompleteChoiceType[]; attributes: MultiAutocompleteChoiceType[];
warehouses: MultiAutocompleteChoiceType[];
data: ExportProductsInput; data: ExportProductsInput;
selectedAttributes: MultiAutocompleteChoiceType[]; selectedAttributes: MultiAutocompleteChoiceType[];
onAttrtibuteSelect: (event: ChangeEvent) => void; onAttrtibuteSelect: FormChange;
onChange: (event: ChangeEvent) => void; onWarehouseSelect: FormChange;
onChange: FormChange;
onFetch: (query: string) => void; onFetch: (query: string) => void;
onSelectAllWarehouses: FormChange;
} }
const ProductExportDialogInfo: React.FC<ProductExportDialogInfoProps> = ({ const ProductExportDialogInfo: React.FC<ProductExportDialogInfoProps> = ({
@ -247,14 +220,18 @@ const ProductExportDialogInfo: React.FC<ProductExportDialogInfoProps> = ({
hasMore, hasMore,
selectedAttributes, selectedAttributes,
loading, loading,
warehouses,
onAttrtibuteSelect, onAttrtibuteSelect,
onWarehouseSelect,
onChange, onChange,
onFetch, onFetch,
onFetchMore onFetchMore,
onSelectAllWarehouses
}) => { }) => {
const classes = useStyles({}); const classes = useStyles({});
const intl = useIntl(); const intl = useIntl();
const [query, onQueryChange] = useSearchQuery(onFetch); const [query, onQueryChange] = useSearchQuery(onFetch);
const getFieldLabel = useProductExportFieldMessages();
const handleFieldChange = (event: ChangeEvent) => const handleFieldChange = (event: ChangeEvent) =>
onChange({ onChange({
@ -289,6 +266,12 @@ const ProductExportDialogInfo: React.FC<ProductExportDialogInfoProps> = ({
} }
}); });
const selectedInventoryFields = data.exportInfo.fields.filter(field =>
inventoryFields.includes(field)
);
const selectedAllInventoryFields =
selectedInventoryFields.length === inventoryFields.length;
return ( return (
<> <>
<Typography className={classes.dialogLabel}> <Typography className={classes.dialogLabel}>
@ -331,6 +314,7 @@ const ProductExportDialogInfo: React.FC<ProductExportDialogInfoProps> = ({
} }
}) })
} }
key={attribute.value}
/> />
))} ))}
{selectedAttributes.length > maxChips && ( {selectedAttributes.length > maxChips && (
@ -408,22 +392,134 @@ const ProductExportDialogInfo: React.FC<ProductExportDialogInfoProps> = ({
onToggleAll={handleToggleAllFields} onToggleAll={handleToggleAllFields}
data-test="financial" data-test="financial"
/> />
<FieldAccordion <Accordion
className={classes.accordion} className={classes.accordion}
title={intl.formatMessage({ title={intl.formatMessage({
defaultMessage: "Inventory Information", defaultMessage: "Inventory Information",
description: "informations about product stock, header" description: "informations about product stock, header"
})} })}
data={data} quickPeek={
fields={[ (data.exportInfo.warehouses.length > 0 ||
ProductFieldEnum.PRODUCT_WEIGHT, selectedInventoryFields.length > 0) && (
ProductFieldEnum.VARIANT_SKU, <div className={classes.quickPeekContainer}>
ProductFieldEnum.VARIANT_WEIGHT {selectedInventoryFields.slice(0, maxChips).map(field => (
]} <Chip
onChange={handleFieldChange} className={classes.chip}
onToggleAll={handleToggleAllFields} label={getFieldLabel(field)}
onClose={() =>
onChange({
target: {
name: field,
value: false
}
})
}
/>
))}
{data.exportInfo.warehouses
.slice(0, maxChips - selectedInventoryFields.length)
.map(warehouseId => (
<Chip
className={classes.chip}
label={
warehouses.find(
warehouse => warehouse.value === warehouseId
).label
}
onClose={() =>
onWarehouseSelect({
target: {
name: warehouseNamePrefix + warehouseId,
value: undefined
}
})
}
/>
))}
{data.exportInfo.warehouses.length +
selectedInventoryFields.length >
maxChips && (
<Typography className={classes.moreLabel} variant="caption">
<FormattedMessage
defaultMessage="and {number} more"
description="there are more elements of list that are hidden"
values={{
number:
data.exportInfo.warehouses.length +
selectedInventoryFields.length -
maxChips
}}
/>
</Typography>
)}
</div>
)
}
data-test="inventory" data-test="inventory"
/> >
<div>
<Option
checked={selectedAllInventoryFields}
name="all"
onChange={() =>
handleToggleAllFields(
inventoryFields,
!selectedAllInventoryFields
)
}
>
<FormattedMessage
defaultMessage="Select All"
description="selectt all options"
/>
</Option>
{inventoryFields.map(field => (
<Option
checked={data.exportInfo.fields.includes(field)}
name={field}
onChange={handleFieldChange}
key={field}
>
{getFieldLabel(field)}
</Option>
))}
</div>
<Hr className={classes.hrWarehouses} />
<Typography>
<FormattedMessage defaultMessage="Export Product Stock Quantity to CSV" />
</Typography>
<div>
<Option
checked={warehouses.every(warehouse =>
data.exportInfo.warehouses.includes(warehouse.value)
)}
name="all-warehouses"
onChange={onSelectAllWarehouses}
>
<FormattedMessage
defaultMessage="Export stock for all warehouses"
description="option"
/>
</Option>
</div>
<Hr className={classes.hrWarehouses} />
<Typography className={classes.warehousesLabel} variant="subtitle1">
<FormattedMessage
defaultMessage="Warehouses A to Z"
description="list of warehouses"
/>
</Typography>
{warehouses.map(warehouse => (
<Option
checked={data.exportInfo.warehouses.includes(warehouse.value)}
name={warehouseNamePrefix + warehouse.value}
onChange={onWarehouseSelect}
key={warehouse.value}
>
{warehouse.label}
</Option>
))}
</Accordion>
<FieldAccordion <FieldAccordion
title={intl.formatMessage({ title={intl.formatMessage({
defaultMessage: "SEO Information", defaultMessage: "SEO Information",

View file

@ -0,0 +1,83 @@
import { ProductFieldEnum } from "@saleor/types/globalTypes";
import { useIntl } from "react-intl";
function useProductExportFieldMessages() {
const intl = useIntl();
const messages = {
[ProductFieldEnum.CATEGORY]: intl.formatMessage({
defaultMessage: "Category",
description: "product field",
id: "productExportFieldCategory"
}),
[ProductFieldEnum.CHARGE_TAXES]: intl.formatMessage({
defaultMessage: "Charge Taxes",
description: "product field",
id: "productExportFieldTaxes"
}),
[ProductFieldEnum.COLLECTIONS]: intl.formatMessage({
defaultMessage: "Collections",
description: "product field",
id: "productExportFieldCollections"
}),
[ProductFieldEnum.COST_PRICE]: intl.formatMessage({
defaultMessage: "Cost Price",
description: "product field",
id: "productExportFieldPrice"
}),
[ProductFieldEnum.DESCRIPTION]: intl.formatMessage({
defaultMessage: "Description",
description: "product field",
id: "productExportFieldDescription"
}),
[ProductFieldEnum.NAME]: intl.formatMessage({
defaultMessage: "Name",
description: "product field",
id: "productExportFieldName"
}),
[ProductFieldEnum.PRODUCT_IMAGES]: intl.formatMessage({
defaultMessage: "Product Images",
description: "product field",
id: "productExportFieldProductImages"
}),
[ProductFieldEnum.PRODUCT_TYPE]: intl.formatMessage({
defaultMessage: "Type",
description: "product field",
id: "productExportFieldType"
}),
[ProductFieldEnum.PRODUCT_WEIGHT]: intl.formatMessage({
defaultMessage: "Export Product Weight",
description: "product field",
id: "productExportFieldProductWeight"
}),
[ProductFieldEnum.VARIANT_IMAGES]: intl.formatMessage({
defaultMessage: "Variant Images",
description: "product field",
id: "productExportFieldVariantImages"
}),
[ProductFieldEnum.VARIANT_PRICE]: intl.formatMessage({
defaultMessage: "Variant Price",
description: "product field",
id: "productExportFieldVariantPrice"
}),
[ProductFieldEnum.VARIANT_SKU]: intl.formatMessage({
defaultMessage: "Export Variant SKU",
description: "product field",
id: "productExportFieldVariantSku"
}),
[ProductFieldEnum.VARIANT_WEIGHT]: intl.formatMessage({
defaultMessage: "Export Variant Weight",
description: "product field",
id: "productExportFieldVariantWeight"
}),
[ProductFieldEnum.VISIBLE]: intl.formatMessage({
defaultMessage: "Visibility",
description: "product field",
id: "productExportFieldVisibility"
})
};
return (field: ProductFieldEnum) => messages[field];
}
export default useProductExportFieldMessages;

View file

@ -167,23 +167,33 @@ export const ProductUpdatePage: React.FC<ProductUpdatePageProps> = ({
const hasVariants = maybe(() => product.productType.hasVariants, false); const hasVariants = maybe(() => product.productType.hasVariants, false);
const handleSubmit = (data: ProductUpdatePageFormData) => { const handleSubmit = (data: ProductUpdatePageFormData) => {
const dataStocks = stocks.map(stock => stock.id); if (product.productType.hasVariants) {
const variantStocks = product.variants[0].stocks.map( onSubmit({
stock => stock.warehouse.id ...data,
); addStocks: [],
const stockDiff = diff(variantStocks, dataStocks); attributes,
removeStocks: [],
updateStocks: []
});
} else {
const dataStocks = stocks.map(stock => stock.id);
const variantStocks = product.variants[0]?.stocks.map(
stock => stock.warehouse.id
);
const stockDiff = diff(variantStocks, dataStocks);
onSubmit({ onSubmit({
...data, ...data,
addStocks: stocks.filter(stock => addStocks: stocks.filter(stock =>
stockDiff.added.some(addedStock => addedStock === stock.id) stockDiff.added.some(addedStock => addedStock === stock.id)
), ),
attributes, attributes,
removeStocks: stockDiff.removed, removeStocks: stockDiff.removed,
updateStocks: stocks.filter( updateStocks: stocks.filter(
stock => !stockDiff.added.some(addedStock => addedStock === stock.id) stock => !stockDiff.added.some(addedStock => addedStock === stock.id)
) )
}); });
}
}; };
return ( return (

View file

@ -1,5 +1,8 @@
import { ProductVariant } from "@saleor/fragments/types/ProductVariant"; import { ProductVariant } from "@saleor/fragments/types/ProductVariant";
import { AttributeInputTypeEnum } from "@saleor/types/globalTypes"; import {
AttributeInputTypeEnum,
WeightUnitsEnum
} from "@saleor/types/globalTypes";
import { warehouseList } from "@saleor/warehouses/fixtures"; import { warehouseList } from "@saleor/warehouses/fixtures";
import { content } from "../storybook/stories/components/RichTextEditor"; import { content } from "../storybook/stories/components/RichTextEditor";
@ -281,7 +284,7 @@ export const product: (
trackInventory: true, trackInventory: true,
weight: { weight: {
__typename: "Weight", __typename: "Weight",
unit: "kg", unit: WeightUnitsEnum.KG,
value: 3 value: 3
} }
}, },
@ -316,14 +319,14 @@ export const product: (
trackInventory: false, trackInventory: false,
weight: { weight: {
__typename: "Weight", __typename: "Weight",
unit: "kg", unit: WeightUnitsEnum.KG,
value: 4 value: 4
} }
} }
], ],
weight: { weight: {
__typename: "Weight", __typename: "Weight",
unit: "kg", unit: WeightUnitsEnum.KG,
value: 5 value: 5
} }
}); });
@ -1672,7 +1675,7 @@ export const variant = (placeholderImage: string): ProductVariant => ({
trackInventory: true, trackInventory: true,
weight: { weight: {
__typename: "Weight", __typename: "Weight",
unit: "kg", unit: WeightUnitsEnum.KG,
value: 6 value: 6
} }
}); });

View file

@ -52,7 +52,10 @@ const ProductUpdate: React.FC<RouteComponentProps<any>> = ({ match }) => {
return ( return (
<ProductUpdateComponent <ProductUpdateComponent
id={decodeURIComponent(match.params.id)} id={decodeURIComponent(match.params.id)}
params={params} params={{
...params,
ids: getArrayQueryParam(qs.ids)
}}
/> />
); );
}; };

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeValueInput, SeoInput, StockInput, ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; import { AttributeValueInput, SeoInput, StockInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductCreate // GraphQL mutation operation: ProductCreate
@ -176,7 +176,7 @@ export interface ProductCreate_productCreate_product_variants {
export interface ProductCreate_productCreate_product_weight { export interface ProductCreate_productCreate_product_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeInputTypeEnum } from "./../../types/globalTypes"; import { AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL query operation: ProductDetails // GraphQL query operation: ProductDetails
@ -170,7 +170,7 @@ export interface ProductDetails_product_variants {
export interface ProductDetails_product_weight { export interface ProductDetails_product_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; import { ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductImageCreate // GraphQL mutation operation: ProductImageCreate
@ -176,7 +176,7 @@ export interface ProductImageCreate_productImageCreate_product_variants {
export interface ProductImageCreate_productImageCreate_product_weight { export interface ProductImageCreate_productImageCreate_product_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; import { ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductImageUpdate // GraphQL mutation operation: ProductImageUpdate
@ -176,7 +176,7 @@ export interface ProductImageUpdate_productImageUpdate_product_variants {
export interface ProductImageUpdate_productImageUpdate_product_weight { export interface ProductImageUpdate_productImageUpdate_product_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeValueInput, SeoInput, ProductErrorCode, AttributeInputTypeEnum } from "./../../types/globalTypes"; import { AttributeValueInput, SeoInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: ProductUpdate // GraphQL mutation operation: ProductUpdate
@ -176,7 +176,7 @@ export interface ProductUpdate_productUpdate_product_variants {
export interface ProductUpdate_productUpdate_product_weight { export interface ProductUpdate_productUpdate_product_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,6 +2,8 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL query operation: ProductVariantDetails // GraphQL query operation: ProductVariantDetails
// ==================================================== // ====================================================
@ -105,7 +107,7 @@ export interface ProductVariantDetails_productVariant_stocks {
export interface ProductVariantDetails_productVariant_weight { export interface ProductVariantDetails_productVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { AttributeValueInput, ProductVariantInput, SeoInput, StockInput, ProductErrorCode, AttributeInputTypeEnum, StockErrorCode } from "./../../types/globalTypes"; import { AttributeValueInput, ProductVariantInput, SeoInput, StockInput, ProductErrorCode, AttributeInputTypeEnum, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: SimpleProductUpdate // GraphQL mutation operation: SimpleProductUpdate
@ -176,7 +176,7 @@ export interface SimpleProductUpdate_productUpdate_product_variants {
export interface SimpleProductUpdate_productUpdate_product_weight { export interface SimpleProductUpdate_productUpdate_product_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -314,7 +314,7 @@ export interface SimpleProductUpdate_productVariantUpdate_productVariant_stocks
export interface SimpleProductUpdate_productVariantUpdate_productVariant_weight { export interface SimpleProductUpdate_productVariantUpdate_productVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -445,7 +445,7 @@ export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_s
export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_weight { export interface SimpleProductUpdate_productVariantStocksCreate_productVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -575,7 +575,7 @@ export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_s
export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_weight { export interface SimpleProductUpdate_productVariantStocksDelete_productVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -706,7 +706,7 @@ export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_s
export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_weight { export interface SimpleProductUpdate_productVariantStocksUpdate_productVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductVariantCreateInput, ProductErrorCode } from "./../../types/globalTypes"; import { ProductVariantCreateInput, ProductErrorCode, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: VariantCreate // GraphQL mutation operation: VariantCreate
@ -113,7 +113,7 @@ export interface VariantCreate_productVariantCreate_productVariant_stocks {
export interface VariantCreate_productVariantCreate_productVariant_weight { export interface VariantCreate_productVariantCreate_productVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes"; import { ProductErrorCode, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: VariantImageAssign // GraphQL mutation operation: VariantImageAssign
@ -113,7 +113,7 @@ export interface VariantImageAssign_variantImageAssign_productVariant_stocks {
export interface VariantImageAssign_variantImageAssign_productVariant_weight { export interface VariantImageAssign_variantImageAssign_productVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ProductErrorCode } from "./../../types/globalTypes"; import { ProductErrorCode, WeightUnitsEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: VariantImageUnassign // GraphQL mutation operation: VariantImageUnassign
@ -113,7 +113,7 @@ export interface VariantImageUnassign_variantImageUnassign_productVariant_stocks
export interface VariantImageUnassign_variantImageUnassign_productVariant_weight { export interface VariantImageUnassign_variantImageUnassign_productVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { StockInput, AttributeValueInput, ProductErrorCode, StockErrorCode } from "./../../types/globalTypes"; import { StockInput, AttributeValueInput, ProductErrorCode, WeightUnitsEnum, StockErrorCode } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: VariantUpdate // GraphQL mutation operation: VariantUpdate
@ -113,7 +113,7 @@ export interface VariantUpdate_productVariantUpdate_productVariant_stocks {
export interface VariantUpdate_productVariantUpdate_productVariant_weight { export interface VariantUpdate_productVariantUpdate_productVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -244,7 +244,7 @@ export interface VariantUpdate_productVariantStocksUpdate_productVariant_stocks
export interface VariantUpdate_productVariantStocksUpdate_productVariant_weight { export interface VariantUpdate_productVariantStocksUpdate_productVariant_weight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -39,6 +39,7 @@ import { ListViews } from "@saleor/types";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import createFilterHandlers from "@saleor/utils/handlers/filterHandlers"; import createFilterHandlers from "@saleor/utils/handlers/filterHandlers";
import { getSortUrlVariables } from "@saleor/utils/sort"; import { getSortUrlVariables } from "@saleor/utils/sort";
import { useWarehouseList } from "@saleor/warehouses/queries";
import React from "react"; import React from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
@ -124,6 +125,11 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
first: 10 first: 10
} }
}); });
const warehouses = useWarehouseList({
variables: {
first: 100
}
});
React.useEffect( React.useEffect(
() => () =>
@ -551,6 +557,11 @@ export const ProductList: React.FC<ProductListProps> = ({ params }) => {
filter: data?.products.totalCount filter: data?.products.totalCount
}} }}
selectedProducts={listElements.length} selectedProducts={listElements.length}
warehouses={
warehouses.data?.warehouses.edges.map(
edge => edge.node
) || []
}
onClose={closeModal} onClose={closeModal}
onSubmit={data => onSubmit={data =>
exportProducts({ exportProducts({

View file

@ -41,6 +41,7 @@ import {
} from "../../../utils/filters"; } from "../../../utils/filters";
import { import {
ProductListUrlFilters, ProductListUrlFilters,
ProductListUrlFiltersAsDictWithMultipleValues,
ProductListUrlFiltersEnum, ProductListUrlFiltersEnum,
ProductListUrlFiltersWithMultipleValues, ProductListUrlFiltersWithMultipleValues,
ProductListUrlQueryParams ProductListUrlQueryParams
@ -295,4 +296,8 @@ export const {
export const { areFiltersApplied, getActiveFilters } = createFilterUtils< export const { areFiltersApplied, getActiveFilters } = createFilterUtils<
ProductListUrlQueryParams, ProductListUrlQueryParams,
ProductListUrlFilters ProductListUrlFilters
>(ProductListUrlFiltersEnum); >({
...ProductListUrlFiltersEnum,
...ProductListUrlFiltersWithMultipleValues,
...ProductListUrlFiltersAsDictWithMultipleValues
});

View file

@ -1,7 +1,7 @@
import { ShippingZoneDetailsFragment } from "@saleor/fragments/types/ShippingZoneDetailsFragment"; import { ShippingZoneDetailsFragment } from "@saleor/fragments/types/ShippingZoneDetailsFragment";
import { ShippingZoneFragment } from "@saleor/fragments/types/ShippingZoneFragment"; import { ShippingZoneFragment } from "@saleor/fragments/types/ShippingZoneFragment";
import { ShippingMethodTypeEnum } from "../types/globalTypes"; import { ShippingMethodTypeEnum, WeightUnitsEnum } from "../types/globalTypes";
export const shippingZones: ShippingZoneFragment[] = [ export const shippingZones: ShippingZoneFragment[] = [
{ {
@ -1554,7 +1554,7 @@ export const shippingZone: ShippingZoneDetailsFragment = {
maximumOrderPrice: null, maximumOrderPrice: null,
maximumOrderWeight: { maximumOrderWeight: {
__typename: "Weight", __typename: "Weight",
unit: "kg", unit: WeightUnitsEnum.KG,
value: 80 value: 80
}, },
minimumOrderPrice: { minimumOrderPrice: {
@ -1564,7 +1564,7 @@ export const shippingZone: ShippingZoneDetailsFragment = {
}, },
minimumOrderWeight: { minimumOrderWeight: {
__typename: "Weight", __typename: "Weight",
unit: "kg", unit: WeightUnitsEnum.KG,
value: 0 value: 0
}, },
name: "DB Schenker", name: "DB Schenker",
@ -1587,7 +1587,7 @@ export const shippingZone: ShippingZoneDetailsFragment = {
}, },
minimumOrderWeight: { minimumOrderWeight: {
__typename: "Weight", __typename: "Weight",
unit: "kg", unit: WeightUnitsEnum.KG,
value: 0 value: 0
}, },
name: "Registred priority", name: "Registred priority",
@ -1610,7 +1610,7 @@ export const shippingZone: ShippingZoneDetailsFragment = {
}, },
minimumOrderWeight: { minimumOrderWeight: {
__typename: "Weight", __typename: "Weight",
unit: "kg", unit: WeightUnitsEnum.KG,
value: 0 value: 0
}, },
@ -1634,7 +1634,7 @@ export const shippingZone: ShippingZoneDetailsFragment = {
}, },
minimumOrderWeight: { minimumOrderWeight: {
__typename: "Weight", __typename: "Weight",
unit: "kg", unit: WeightUnitsEnum.KG,
value: 0 value: 0
}, },
name: "DHL", name: "DHL",

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ShippingPriceInput, ShippingErrorCode, ShippingMethodTypeEnum } from "./../../types/globalTypes"; import { ShippingPriceInput, ShippingErrorCode, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: CreateShippingRate // GraphQL mutation operation: CreateShippingRate
@ -28,7 +28,7 @@ export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMet
export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_minimumOrderWeight { export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_minimumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -40,7 +40,7 @@ export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMet
export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_maximumOrderWeight { export interface CreateShippingRate_shippingPriceCreate_shippingZone_shippingMethods_maximumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ShippingErrorCode, ShippingMethodTypeEnum } from "./../../types/globalTypes"; import { ShippingErrorCode, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: DeleteShippingRate // GraphQL mutation operation: DeleteShippingRate
@ -28,7 +28,7 @@ export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMet
export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_minimumOrderWeight { export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_minimumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -40,7 +40,7 @@ export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMet
export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_maximumOrderWeight { export interface DeleteShippingRate_shippingPriceDelete_shippingZone_shippingMethods_maximumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ShippingMethodTypeEnum } from "./../../types/globalTypes"; import { WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL query operation: ShippingZone // GraphQL query operation: ShippingZone
@ -22,7 +22,7 @@ export interface ShippingZone_shippingZone_shippingMethods_minimumOrderPrice {
export interface ShippingZone_shippingZone_shippingMethods_minimumOrderWeight { export interface ShippingZone_shippingZone_shippingMethods_minimumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -34,7 +34,7 @@ export interface ShippingZone_shippingZone_shippingMethods_maximumOrderPrice {
export interface ShippingZone_shippingZone_shippingMethods_maximumOrderWeight { export interface ShippingZone_shippingZone_shippingMethods_maximumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -2,7 +2,7 @@
/* eslint-disable */ /* eslint-disable */
// This file was automatically generated and should not be edited. // This file was automatically generated and should not be edited.
import { ShippingPriceInput, ShippingErrorCode, ShippingMethodTypeEnum } from "./../../types/globalTypes"; import { ShippingPriceInput, ShippingErrorCode, WeightUnitsEnum, ShippingMethodTypeEnum } from "./../../types/globalTypes";
// ==================================================== // ====================================================
// GraphQL mutation operation: UpdateShippingRate // GraphQL mutation operation: UpdateShippingRate
@ -22,7 +22,7 @@ export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_minimumOr
export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_minimumOrderWeight { export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_minimumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }
@ -34,7 +34,7 @@ export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_maximumOr
export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_maximumOrderWeight { export interface UpdateShippingRate_shippingPriceUpdate_shippingMethod_maximumOrderWeight {
__typename: "Weight"; __typename: "Weight";
unit: string; unit: WeightUnitsEnum;
value: number; value: number;
} }

View file

@ -38,8 +38,8 @@ const useStyles = makeStyles(
width: 47 width: 47
}, },
avatarDefault: { avatarDefault: {
"& p": { "& div": {
color: "#fff", color: theme.palette.primary.contrastText,
lineHeight: "47px" lineHeight: "47px"
}, },
background: theme.palette.primary.main, background: theme.palette.primary.main,

View file

@ -35,7 +35,7 @@ const useStyles = makeStyles(
width: 120 width: 120
}, },
avatarDefault: { avatarDefault: {
"& p": { "& div": {
color: "#fff", color: "#fff",
fontSize: 35, fontSize: 35,
fontWeight: "bold", fontWeight: "bold",

View file

@ -34,6 +34,9 @@ const staffList = gql`
cursor cursor
node { node {
...StaffMemberFragment ...StaffMemberFragment
avatar(size: 48) {
url
}
} }
} }
pageInfo { pageInfo {

View file

@ -14,11 +14,6 @@ export interface StaffMemberAdd_staffCreate_errors {
field: string | null; field: string | null;
} }
export interface StaffMemberAdd_staffCreate_user_avatar {
__typename: "Image";
url: string;
}
export interface StaffMemberAdd_staffCreate_user_permissionGroups { export interface StaffMemberAdd_staffCreate_user_permissionGroups {
__typename: "Group"; __typename: "Group";
id: string; id: string;
@ -32,6 +27,11 @@ export interface StaffMemberAdd_staffCreate_user_userPermissions {
name: string; name: string;
} }
export interface StaffMemberAdd_staffCreate_user_avatar {
__typename: "Image";
url: string;
}
export interface StaffMemberAdd_staffCreate_user { export interface StaffMemberAdd_staffCreate_user {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -39,9 +39,9 @@ export interface StaffMemberAdd_staffCreate_user {
firstName: string; firstName: string;
isActive: boolean; isActive: boolean;
lastName: string; lastName: string;
avatar: StaffMemberAdd_staffCreate_user_avatar | null;
permissionGroups: (StaffMemberAdd_staffCreate_user_permissionGroups | null)[] | null; permissionGroups: (StaffMemberAdd_staffCreate_user_permissionGroups | null)[] | null;
userPermissions: (StaffMemberAdd_staffCreate_user_userPermissions | null)[] | null; userPermissions: (StaffMemberAdd_staffCreate_user_userPermissions | null)[] | null;
avatar: StaffMemberAdd_staffCreate_user_avatar | null;
} }
export interface StaffMemberAdd_staffCreate { export interface StaffMemberAdd_staffCreate {

View file

@ -8,11 +8,6 @@ import { PermissionEnum } from "./../../types/globalTypes";
// GraphQL query operation: StaffMemberDetails // GraphQL query operation: StaffMemberDetails
// ==================================================== // ====================================================
export interface StaffMemberDetails_user_avatar {
__typename: "Image";
url: string;
}
export interface StaffMemberDetails_user_permissionGroups { export interface StaffMemberDetails_user_permissionGroups {
__typename: "Group"; __typename: "Group";
id: string; id: string;
@ -26,6 +21,11 @@ export interface StaffMemberDetails_user_userPermissions {
name: string; name: string;
} }
export interface StaffMemberDetails_user_avatar {
__typename: "Image";
url: string;
}
export interface StaffMemberDetails_user { export interface StaffMemberDetails_user {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -33,9 +33,9 @@ export interface StaffMemberDetails_user {
firstName: string; firstName: string;
isActive: boolean; isActive: boolean;
lastName: string; lastName: string;
avatar: StaffMemberDetails_user_avatar | null;
permissionGroups: (StaffMemberDetails_user_permissionGroups | null)[] | null; permissionGroups: (StaffMemberDetails_user_permissionGroups | null)[] | null;
userPermissions: (StaffMemberDetails_user_userPermissions | null)[] | null; userPermissions: (StaffMemberDetails_user_userPermissions | null)[] | null;
avatar: StaffMemberDetails_user_avatar | null;
} }
export interface StaffMemberDetails { export interface StaffMemberDetails {

View file

@ -14,11 +14,6 @@ export interface StaffMemberUpdate_staffUpdate_errors {
field: string | null; field: string | null;
} }
export interface StaffMemberUpdate_staffUpdate_user_avatar {
__typename: "Image";
url: string;
}
export interface StaffMemberUpdate_staffUpdate_user_permissionGroups { export interface StaffMemberUpdate_staffUpdate_user_permissionGroups {
__typename: "Group"; __typename: "Group";
id: string; id: string;
@ -32,6 +27,11 @@ export interface StaffMemberUpdate_staffUpdate_user_userPermissions {
name: string; name: string;
} }
export interface StaffMemberUpdate_staffUpdate_user_avatar {
__typename: "Image";
url: string;
}
export interface StaffMemberUpdate_staffUpdate_user { export interface StaffMemberUpdate_staffUpdate_user {
__typename: "User"; __typename: "User";
id: string; id: string;
@ -39,9 +39,9 @@ export interface StaffMemberUpdate_staffUpdate_user {
firstName: string; firstName: string;
isActive: boolean; isActive: boolean;
lastName: string; lastName: string;
avatar: StaffMemberUpdate_staffUpdate_user_avatar | null;
permissionGroups: (StaffMemberUpdate_staffUpdate_user_permissionGroups | null)[] | null; permissionGroups: (StaffMemberUpdate_staffUpdate_user_permissionGroups | null)[] | null;
userPermissions: (StaffMemberUpdate_staffUpdate_user_userPermissions | null)[] | null; userPermissions: (StaffMemberUpdate_staffUpdate_user_userPermissions | null)[] | null;
avatar: StaffMemberUpdate_staffUpdate_user_avatar | null;
} }
export interface StaffMemberUpdate_staffUpdate { export interface StaffMemberUpdate_staffUpdate {

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,15 @@
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
import Checkbox from "@material-ui/core/Checkbox";
import { createMuiTheme, Theme } from "@material-ui/core/styles"; import { createMuiTheme, Theme } from "@material-ui/core/styles";
import { darken, fade } from "@material-ui/core/styles/colorManipulator"; import { darken, fade } from "@material-ui/core/styles/colorManipulator";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import { createElement } from "react";
import { IThemeColors } from "./components/Theme/themes"; import { IThemeColors } from "./components/Theme/themes";
import CheckboxIcon from "./icons/Checkbox";
import CheckboxCheckedIcon from "./icons/CheckboxChecked";
import CheckboxIndeterminateIcon from "./icons/CheckboxIndeterminate";
const createShadow = (pv, pb, ps, uv, ub, us, av, ab, as) => const createShadow = (pv, pb, ps, uv, ub, us, av, ab, as) =>
[ [
@ -562,3 +567,10 @@ Card.defaultProps = {
Typography.defaultProps = { Typography.defaultProps = {
component: "div" component: "div"
}; };
Checkbox.defaultProps = {
checkedIcon: createElement(CheckboxCheckedIcon),
color: "primary",
icon: createElement(CheckboxIcon),
indeterminateIcon: createElement(CheckboxIndeterminateIcon)
};

View file

@ -865,7 +865,9 @@ export enum WebhookErrorCode {
export enum WebhookEventTypeEnum { export enum WebhookEventTypeEnum {
ANY_EVENTS = "ANY_EVENTS", ANY_EVENTS = "ANY_EVENTS",
CHECKOUT_CREATED = "CHECKOUT_CREATED",
CHECKOUT_QUANTITY_CHANGED = "CHECKOUT_QUANTITY_CHANGED", CHECKOUT_QUANTITY_CHANGED = "CHECKOUT_QUANTITY_CHANGED",
CHECKOUT_UPDATED = "CHECKOUT_UPDATED",
CUSTOMER_CREATED = "CUSTOMER_CREATED", CUSTOMER_CREATED = "CUSTOMER_CREATED",
FULFILLMENT_CREATED = "FULFILLMENT_CREATED", FULFILLMENT_CREATED = "FULFILLMENT_CREATED",
INVOICE_DELETED = "INVOICE_DELETED", INVOICE_DELETED = "INVOICE_DELETED",

View file

@ -32,6 +32,14 @@ const WebhookEvents: React.FC<WebhookEventsProps> = ({
defaultMessage: "All events", defaultMessage: "All events",
description: "event" description: "event"
}), }),
[WebhookEventTypeEnum.CHECKOUT_CREATED]: intl.formatMessage({
defaultMessage: "Checkout created",
description: "event"
}),
[WebhookEventTypeEnum.CHECKOUT_UPDATED]: intl.formatMessage({
defaultMessage: "Checkout updated",
description: "event"
}),
[WebhookEventTypeEnum.CUSTOMER_CREATED]: intl.formatMessage({ [WebhookEventTypeEnum.CUSTOMER_CREATED]: intl.formatMessage({
defaultMessage: "Customer created", defaultMessage: "Customer created",
description: "event" description: "event"