diff --git a/imports/client/ui/components/Sidebar/Sidebar.js b/imports/client/ui/components/Sidebar/Sidebar.js index d3973c3510..0cfe6d9638 100644 --- a/imports/client/ui/components/Sidebar/Sidebar.js +++ b/imports/client/ui/components/Sidebar/Sidebar.js @@ -1,10 +1,8 @@ import React from "react"; import { compose, withState } from "recompose"; import { NavLink, withRouter } from "react-router-dom"; -import classNames from "classnames"; import PropTypes from "prop-types"; import AppBar from "@material-ui/core/AppBar"; -import Collapse from "@material-ui/core/Collapse"; import Fab from "@material-ui/core/Fab"; import Hidden from "@material-ui/core/Hidden"; import Toolbar from "@material-ui/core/Toolbar"; @@ -14,7 +12,6 @@ import ListItemIcon from "@material-ui/core/ListItemIcon"; import ListItemText from "@material-ui/core/ListItemText"; import Drawer from "@material-ui/core/Drawer"; import withStyles from "@material-ui/core/styles/withStyles"; -import SettingsIcon from "mdi-material-ui/Settings"; import CloseIcon from "mdi-material-ui/Close"; import { Translation } from "/imports/plugins/core/ui/client/components"; import useIsAppLoading from "/imports/client/ui/hooks/useIsAppLoading.js"; @@ -97,7 +94,6 @@ const styles = (theme) => ({ function Sidebar(props) { const { classes, - history, isMobile, isSidebarOpen, onDrawerClose, @@ -108,7 +104,6 @@ function Sidebar(props) { const [isAppLoading] = useIsAppLoading(); const [currentShopId] = useCurrentShopId(); const primaryRoutes = useOperatorRoutes({ groups: ["navigation"] }); - const settingRoutes = useOperatorRoutes({ groups: ["settings"] }); let drawerProps = { classes: { @@ -138,7 +133,7 @@ function Sidebar(props) { { setIsSettingsOpen(false); @@ -158,53 +153,6 @@ function Sidebar(props) { ))} - - { - // Push the first setting route when opened, but not on mobile - if (!isSettingsOpen && !isMobile) { - const [firstRoute] = settingRoutes; - - if (firstRoute) { - history.push(firstRoute.path); - } - } - setIsSettingsOpen(!isSettingsOpen); - }} - > - - - - - - - - - - {settingRoutes.map((route) => ( - - - - - - - - ))} - ); } diff --git a/imports/client/ui/layouts/ContentViewFullLayout.js b/imports/client/ui/layouts/ContentViewFullLayout.js index 084a7eecc7..df368efe3a 100644 --- a/imports/client/ui/layouts/ContentViewFullLayout.js +++ b/imports/client/ui/layouts/ContentViewFullLayout.js @@ -1,6 +1,6 @@ /** - * Component provies a fill width and height, non-scrollable container - * for dashboard layouts that want to defin their on scroll zones. + * Component provides a fill width and height, non-scrollable container + * for dashboard layouts that want to define their on scroll zones. */ import React from "react"; import PropTypes from "prop-types"; diff --git a/imports/client/ui/layouts/ContentViewPrimaryDetailLayout.js b/imports/client/ui/layouts/ContentViewPrimaryDetailLayout.js index d269eec016..023c5d9dfd 100644 --- a/imports/client/ui/layouts/ContentViewPrimaryDetailLayout.js +++ b/imports/client/ui/layouts/ContentViewPrimaryDetailLayout.js @@ -44,6 +44,7 @@ const useStyles = makeStyles((theme) => ({ }, sidebar: { flex: "1 1 auto", + minWidth: 330, maxWidth: 330, height: `calc(100vh - ${theme.mixins.toolbar.minHeight}px)`, overflowY: "auto", diff --git a/imports/plugins/core/address/client/components/AddressValidationSettingsForm.js b/imports/plugins/core/address/client/components/AddressValidationSettingsForm.js index 6fca12602a..20fdb9683d 100644 --- a/imports/plugins/core/address/client/components/AddressValidationSettingsForm.js +++ b/imports/plugins/core/address/client/components/AddressValidationSettingsForm.js @@ -9,7 +9,7 @@ import Button from "@reactioncommerce/catalyst/Button"; import Grid from "@material-ui/core/Grid"; import MenuItem from "@material-ui/core/MenuItem"; import TextField from "@material-ui/core/TextField"; -import { makeStyles } from "@material-ui/styles"; +import { makeStyles } from "@material-ui/core"; import muiOptions from "reacto-form/cjs/muiOptions"; import useReactoForm from "reacto-form/cjs/useReactoForm"; @@ -217,7 +217,7 @@ export default function AddressValidationSettingsForm(props) { } + + + + ); +} + +ShopAddressForm.propTypes = { + isEditMode: PropTypes.bool.isRequired, + isInitialView: PropTypes.bool.isRequired, + setIsEditMode: PropTypes.func.isRequired +}; + +export default ShopAddressForm; diff --git a/imports/plugins/core/dashboard/client/components/ShopAddressSettings.js b/imports/plugins/core/dashboard/client/components/ShopAddressSettings.js new file mode 100644 index 0000000000..21977fba88 --- /dev/null +++ b/imports/plugins/core/dashboard/client/components/ShopAddressSettings.js @@ -0,0 +1,104 @@ +import React, { Fragment, useState } from "react"; +import i18next from "i18next"; +import { + Box, + Button, + Card, + CardContent, + CardHeader, + CircularProgress, + makeStyles, + Typography +} from "@material-ui/core"; +import useShopSettings from "../hooks/useShopSettings"; +import ShopAddressForm from "./ShopAddressForm"; + +const useStyles = makeStyles((theme) => ({ + card: { + marginTop: theme.spacing(3), + marginBottom: theme.spacing(3) + }, + textField: { + minWidth: 350 + }, + saveButton: { + textAlign: "right" + } +})); + +/** + * Shop address settings form block component + * @returns {Node} React node + */ +function ShopAddressSettings() { + const classes = useStyles(); + const [isEditMode, setIsEditMode] = useState(false); + const { loading, shop: { addressBook } } = useShopSettings(); + let readOnlyMode = false; + let isInitialView = false; + let address = null; + + if (loading) return ; + + // If an address has been set, render in read only mode + if (addressBook && addressBook[0]) { + ([address] = addressBook); + + if (!isEditMode) readOnlyMode = true; + } else { + isInitialView = true; + } + + const actionButtonText = isInitialView + ? i18next.t("admin.settings.address.addNewAddress") + : i18next.t("admin.settings.address.edit"); + + const handleOnEdit = () => { + setIsEditMode(true); + }; + + return ( + + + + {isInitialView && + + {i18next.t("admin.settings.address.initialViewText")} + + } + { + (readOnlyMode) + ? + + {address.company} + {address.fullName} + {address.address1} + {address.address2} + {`${address.city}, ${address.region} ${address.postal}`} + {address.country} + {address.phone} + + : + + } + {!isEditMode && + + + + } + + + ); +} + +export default ShopAddressSettings; diff --git a/imports/plugins/core/dashboard/client/components/ShopLogoUrls.js b/imports/plugins/core/dashboard/client/components/ShopLogoUrls.js index 07c8df478e..43813a27cf 100644 --- a/imports/plugins/core/dashboard/client/components/ShopLogoUrls.js +++ b/imports/plugins/core/dashboard/client/components/ShopLogoUrls.js @@ -17,6 +17,11 @@ import { Components } from "@reactioncommerce/reaction-components"; import { i18next } from "/client/api"; import withShop from "/imports/plugins/core/graphql/lib/hocs/withShop"; +const CardContainer = styled(Card)` + margin-bottom: 24px + margin-top: 24px +`; + const PaddedField = styled(Field)` margin-bottom: 30px; `; @@ -85,7 +90,7 @@ class ShopLogoUrls extends Component { const { primaryShopLogoUrl } = shopLogoUrls || {}; return ( - + - + ); } } diff --git a/imports/plugins/core/dashboard/client/components/ShopSettingsForm.js b/imports/plugins/core/dashboard/client/components/ShopSettingsForm.js new file mode 100644 index 0000000000..b4f96e412a --- /dev/null +++ b/imports/plugins/core/dashboard/client/components/ShopSettingsForm.js @@ -0,0 +1,179 @@ +import React, { useState } from "react"; +import i18next from "i18next"; +import SimpleSchema from "simpl-schema"; +import Button from "@reactioncommerce/catalyst/Button"; +import TextField from "@reactioncommerce/catalyst/TextField"; +import useReactoForm from "reacto-form/cjs/useReactoForm"; +import muiCheckboxOptions from "reacto-form/esm/muiCheckboxOptions"; +import muiOptions from "reacto-form/cjs/muiOptions"; +import { + Card, + CardContent, + CardHeader, + CircularProgress, + FormControlLabel, + Grid, + makeStyles, + Checkbox +} from "@material-ui/core"; +import useShopSettings from "../hooks/useShopSettings"; + +const useStyles = makeStyles((theme) => ({ + card: { + marginTop: theme.spacing(3), + marginBottom: theme.spacing(3) + }, + saveButton: { + textAlign: "right" + } +})); + +const shopSettings = new SimpleSchema({ + "allowGuestCheckout": { + type: Boolean, + optional: true + }, + "name": { + type: String, + min: 1 + }, + "emails": { + type: Array + }, + "emails.$": new SimpleSchema({ + address: { + type: String, + regEx: SimpleSchema.RegEx.Email + } + }), + "slug": { + type: String, + min: 1 + }, + "description": { + type: String, + optional: true + }, + "keywords": { + type: String, + optional: true + } +}); + +const validator = shopSettings.getFormValidator(); + +/** + * Shop settings form block component + * @returns {Node} React node + */ +export default function ShopSettings() { + const classes = useStyles(); + const [isSubmitting, setIsSubmitting] = useState(false); + const { loading, onUpdateShop, shop } = useShopSettings(); + const { + getFirstErrorMessage, + getInputProps, + hasErrors, + submitForm + } = useReactoForm({ + async onSubmit(formData) { + setIsSubmitting(true); + await onUpdateShop(shopSettings.clean(formData)); + setIsSubmitting(false); + }, + validator(formData) { + return validator(shopSettings.clean(formData)); + }, + value: shop + }); + + if (loading) return ; + + const handleSubmit = (event) => { + event.preventDefault(); + submitForm(); + }; + + return ( + + + + + + + + + + + + + + + + + + + + + + } + label={i18next.t("admin.settings.shop.allowGuestCheckout")} + {...getInputProps("allowGuestCheckout", muiCheckboxOptions)} + /> + + + + + + + + ); +} diff --git a/imports/plugins/core/dashboard/client/components/ShopSettingsRegion.js b/imports/plugins/core/dashboard/client/components/ShopSettingsRegion.js new file mode 100644 index 0000000000..3bc6b0df13 --- /dev/null +++ b/imports/plugins/core/dashboard/client/components/ShopSettingsRegion.js @@ -0,0 +1,30 @@ +import React from "react"; +import i18next from "i18next"; +import { Blocks } from "@reactioncommerce/reaction-components"; +import { + makeStyles, + Typography +} from "@material-ui/core"; + +const useStyles = makeStyles((theme) => ({ + header: { + marginBottom: theme.spacing(4) + } +})); + +/** + * @summary Renders payment settings page + * @param {Object} props Component props + * @return {React.Node} React node + */ +export default function ShopSettingsRegion(props) { + const classes = useStyles(); + return ( + <> + + {i18next.t("admin.settings.shop.header")} + + + + ); +} diff --git a/imports/plugins/core/dashboard/client/components/StorefrontUrls.js b/imports/plugins/core/dashboard/client/components/StorefrontUrls.js index d8197a04ab..2e992ae57f 100644 --- a/imports/plugins/core/dashboard/client/components/StorefrontUrls.js +++ b/imports/plugins/core/dashboard/client/components/StorefrontUrls.js @@ -1,17 +1,18 @@ -import React, { Component, Fragment } from "react"; +import React, { Component } from "react"; import PropTypes from "prop-types"; import gql from "graphql-tag"; -import styled from "styled-components"; import { Form } from "reacto-form"; import { Mutation } from "react-apollo"; import { compose } from "recompose"; import withStyles from "@material-ui/core/styles/withStyles"; -import Button from "@material-ui/core/Button"; -import Card from "@material-ui/core/Card"; -import CardHeader from "@material-ui/core/CardHeader"; -import CardActions from "@material-ui/core/CardActions"; -import CardContent from "@material-ui/core/CardContent"; -import Grid from "@material-ui/core/Grid"; +import { Button } from "@reactioncommerce/catalyst"; +import { + Box, + Card, + CardHeader, + CardActions, + CardContent +} from "@material-ui/core"; import Typography from "@material-ui/core/Typography"; import ErrorsBlock from "@reactioncommerce/components/ErrorsBlock/v1"; import Field from "@reactioncommerce/components/Field/v1"; @@ -20,17 +21,15 @@ import { i18next } from "/client/api"; import withPrimaryShopId from "/imports/plugins/core/graphql/lib/hocs/withPrimaryShopId"; import withShop from "/imports/plugins/core/graphql/lib/hocs/withShop"; -const PaddedField = styled(Field)` - margin-bottom: 30px; -`; - -const RightAlignedGrid = styled(Grid)` - text-align: right; -`; - -const styles = () => ({ +const styles = (theme) => ({ + card: { + marginBottom: theme.spacing(3) + }, helpText: { marginTop: "10px" + }, + field: { + marginBottom: theme.spacing(4) } }); @@ -107,7 +106,7 @@ class StorefrontUrls extends Component { const { storefrontHomeUrl, storefrontLoginUrl, storefrontOrderUrl, storefrontOrdersUrl, storefrontAccountProfileUrl } = storefrontUrls || {}; return ( - + {(mutationFunc) => ( - -
{ - this.form = formRef; - }} - onChange={this.handleFormChange} - onSubmit={(data) => this.handleUpdateUrls(data, mutationFunc)} - value={shop} - > - - { + this.form = formRef; + }} + onChange={this.handleFormChange} + onSubmit={(data) => this.handleUpdateUrls(data, mutationFunc)} + value={shop} + > + + + - - - - + + + + - - - - + + + + - - - {i18next.t( - "shopSettings.storefrontUrls.storefrontOrderUrlHelpText", - "In order for links inside of order emails to work, you must provide both an `:orderId` and `:token` in this field. These act as placeholders that are replaced with the correct data in your email template when an order email is generated. For example: http://shop.example.com/my-orders/:orderId?token=:token" - )} - - - - + + {i18next.t( + "shopSettings.storefrontUrls.storefrontOrderUrlHelpText", + "In order for links inside of order emails to work, you must provide both an `:orderId` and `:token` in this field. These act as placeholders that are replaced with the correct data in your email template when an order email is generated. For example: http://shop.example.com/my-orders/:orderId?token=:token" + )} + + + + + - - - - + + + + - - - - - - - - - - - -
-
+ placeholder={ + i18next.t( + "shopSettings.storefrontUrls.storefrontAccountProfileUrlDescription", + "URL of your shops account profile homepage" + ) + } + value={storefrontAccountProfileUrl || ""} + /> + + + + + + + + + )}
diff --git a/imports/plugins/core/dashboard/client/components/SystemSettingsRegion.js b/imports/plugins/core/dashboard/client/components/SystemSettingsRegion.js new file mode 100644 index 0000000000..455dfea0ad --- /dev/null +++ b/imports/plugins/core/dashboard/client/components/SystemSettingsRegion.js @@ -0,0 +1,30 @@ +import React from "react"; +import i18next from "i18next"; +import { Blocks } from "@reactioncommerce/reaction-components"; +import { + makeStyles, + Typography +} from "@material-ui/core"; + +const useStyles = makeStyles((theme) => ({ + header: { + marginBottom: theme.spacing(4) + } +})); + +/** + * @summary Renders payment settings page + * @param {Object} props Component props + * @return {React.Node} React node + */ +export default function SystemSettingsRegion(props) { + const classes = useStyles(); + return ( + <> + + {i18next.t("admin.settings.system.header")} + + + + ); +} diff --git a/imports/plugins/core/dashboard/client/graphql/fragments/shopCommon.js b/imports/plugins/core/dashboard/client/graphql/fragments/shopCommon.js new file mode 100644 index 0000000000..e0a4706827 --- /dev/null +++ b/imports/plugins/core/dashboard/client/graphql/fragments/shopCommon.js @@ -0,0 +1,29 @@ +import gql from "graphql-tag"; + +export default gql` + fragment ShopCommon on Shop { + _id + allowGuestCheckout + addressBook { + company + fullName + address1 + address2 + city + region + postal + country + phone + isCommercial + } + description + emails { + address + } + keywords + name + slug + } +`; + + diff --git a/imports/plugins/core/dashboard/client/graphql/mutations/updateShop.js b/imports/plugins/core/dashboard/client/graphql/mutations/updateShop.js new file mode 100644 index 0000000000..7bd23cbd79 --- /dev/null +++ b/imports/plugins/core/dashboard/client/graphql/mutations/updateShop.js @@ -0,0 +1,13 @@ +import gql from "graphql-tag"; +import shopCommon from "../fragments/shopCommon"; + +export default gql` + mutation updateShop($input: UpdateShopInput!) { + updateShop(input: $input) { + shop { + ...ShopCommon + } + } + } + ${shopCommon} +`; diff --git a/imports/plugins/core/dashboard/client/graphql/queries/shop.js b/imports/plugins/core/dashboard/client/graphql/queries/shop.js new file mode 100644 index 0000000000..ed94a40c2a --- /dev/null +++ b/imports/plugins/core/dashboard/client/graphql/queries/shop.js @@ -0,0 +1,11 @@ +import gql from "graphql-tag"; +import shopCommonFragment from "../fragments/shopCommon"; + +export default gql` + query shopQuery($id: ID!) { + shop(id: $id) { + ...ShopCommon + } + } + ${shopCommonFragment} +`; diff --git a/imports/plugins/core/dashboard/client/hooks/useShopSettings.js b/imports/plugins/core/dashboard/client/hooks/useShopSettings.js new file mode 100644 index 0000000000..3031370d8e --- /dev/null +++ b/imports/plugins/core/dashboard/client/hooks/useShopSettings.js @@ -0,0 +1,68 @@ +import i18next from "i18next"; +import { useMutation, useLazyQuery } from "@apollo/react-hooks"; +import { useSnackbar } from "notistack"; +import useCurrentShopId from "/imports/client/ui/hooks/useCurrentShopId"; +import shopQuery from "../graphql/queries/shop"; +import updateShopMutation from "../graphql/mutations/updateShop"; + +/** + * @method useShopSettings + * @summary useShopSettings hook + * @param {Object} args input arguments + * @param {String} args.shopId Id of the shop to update settings on + * @param {Object} args.fields Shop setting fields to update + * @returns {Object} The updated shop object + */ +function useShopSettings(args = {}) { + const { + shopId: providedShopId + } = args; + const [currentShopId] = useCurrentShopId(); + const [updateShop] = useMutation(updateShopMutation); + const shopId = providedShopId || currentShopId; + const { enqueueSnackbar } = useSnackbar(); + const [fetchShop, { called, loading, data: shopQueryResult, refetch: refetchShopQuery }] = useLazyQuery(shopQuery); + + if (shopId && !called) { + fetchShop({ + variables: { + id: shopId + } + }); + } + + /** + * @method onUpdateShop + * @param {Object} fields Fields in `UpdateShopInput` that can be updated. + * @param {String} .shopId Shop ID of the shop to update settings for. Leave blank for current shop. + * @returns {undefined} no return value + */ + const onUpdateShop = async ({ + shopId: shopIdLocal = shopId, + ...fields + }) => { + try { + await updateShop({ + variables: { + input: { + ...fields, + shopId: shopIdLocal + } + } + }); + enqueueSnackbar(i18next.t("admin.settings.saveSuccess"), { variant: "success" }); + } catch (error) { + enqueueSnackbar(i18next.t("admin.settings.saveFailed"), { variant: "error" }); + } + }; + + return { + loading, + onUpdateShop, + refetchShopQuery, + shop: (shopQueryResult && shopQueryResult.shop) || {}, + shopId + }; +} + +export default useShopSettings; diff --git a/imports/plugins/core/dashboard/client/index.js b/imports/plugins/core/dashboard/client/index.js index 25e4c7de1c..ccbde058e6 100644 --- a/imports/plugins/core/dashboard/client/index.js +++ b/imports/plugins/core/dashboard/client/index.js @@ -1,13 +1,18 @@ -import React from "react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faStore } from "@fortawesome/free-solid-svg-icons"; - -import { registerBlock } from "/imports/plugins/core/components/lib"; +import SettingsIcon from "mdi-material-ui/Settings"; +import { registerBlock } from "@reactioncommerce/reaction-components"; import { registerOperatorRoute } from "/imports/client/ui"; import OperatorLanding from "/imports/plugins/core/dashboard/client/components/OperatorLanding"; +// Settings block regions +import ShopSettingsRegion from "./components/ShopSettingsRegion"; +import SystemSettingsRegion from "./components/SystemSettingsRegion"; + +// Settings import SystemInformation from "./components/SystemInformation"; import ShopLogoUrls from "./components/ShopLogoUrls"; +import SettingsDashboard from "./components/SettingsDashboard"; +import ShopSettingsForm from "./components/ShopSettingsForm"; +import ShopAddressSettings from "./components/ShopAddressSettings"; import StorefrontUrls from "./components/StorefrontUrls"; import "./components/shopBrandImageOption"; @@ -25,33 +30,67 @@ registerOperatorRoute({ }); registerOperatorRoute({ - group: "settings", - priority: 10, - path: "/shop-settings", - MainComponent: "shopSettings", + group: "navigation", + priority: 80, + path: "/settings/:setting?", + href: "/settings/shop", + LayoutComponent: null, + MainComponent: SettingsDashboard, // eslint-disable-next-line react/display-name - SidebarIconComponent: (props) => , + SidebarIconComponent: SettingsIcon, + sidebarI18nLabel: "admin.settings.settingsLabel" +}); + +// Shop settings region +registerOperatorRoute({ + group: "settings", + MainComponent: ShopSettingsRegion, + priority: 110, + path: "/settings/shop", sidebarI18nLabel: "admin.settings.shopSettingsLabel" }); registerOperatorRoute({ group: "settings", - MainComponent: SystemInformation, - path: "/system", - priority: 1000, + MainComponent: SystemSettingsRegion, + path: "/settings/system", + priority: 300, sidebarI18nLabel: "shopSettings.systemInfo.title" }); +// Settings blocks registerBlock({ region: "ShopSettings", - name: "ShopLogoUrls", - component: ShopLogoUrls, + name: "ShopSettingsGeneral", + component: ShopSettingsForm, + priority: 1 +}); + +registerBlock({ + region: "ShopSettings", + name: "ShopAddress", + component: ShopAddressSettings, priority: 2 }); registerBlock({ region: "ShopSettings", + name: "ShopLogoUrls", + component: ShopLogoUrls, + priority: 3 +}); + +registerBlock({ + region: "EmailSettings", name: "StorefrontUrls", component: StorefrontUrls, - priority: 3 + priority: 2 +}); + +// System settings blocks +registerBlock({ + region: "SystemSettings", + name: "SystemSettingsGeneral", + component: SystemInformation, + priority: 1 }); diff --git a/imports/plugins/core/email/client/components/EmailSettingsRegion.js b/imports/plugins/core/email/client/components/EmailSettingsRegion.js new file mode 100644 index 0000000000..db86e98c34 --- /dev/null +++ b/imports/plugins/core/email/client/components/EmailSettingsRegion.js @@ -0,0 +1,30 @@ +import React from "react"; +import i18next from "i18next"; +import { Blocks } from "@reactioncommerce/reaction-components"; +import { + makeStyles, + Typography +} from "@material-ui/core"; + +const useStyles = makeStyles((theme) => ({ + header: { + marginBottom: theme.spacing(4) + } +})); + +/** + * @summary Renders payment settings page + * @param {Object} props Component props + * @return {React.Node} React node + */ +export default function EmailSettingsRegion(props) { + const classes = useStyles(); + return ( + <> + + {i18next.t("admin.settings.email.header")} + + + + ); +} diff --git a/imports/plugins/core/email/client/containers/EmailSettings.js b/imports/plugins/core/email/client/containers/EmailSettings.js index e3098c09dc..507f126e45 100644 --- a/imports/plugins/core/email/client/containers/EmailSettings.js +++ b/imports/plugins/core/email/client/containers/EmailSettings.js @@ -1,18 +1,12 @@ -import React, { Component, Fragment } from "react"; -import { Blocks } from "@reactioncommerce/reaction-components"; +import React, { Component } from "react"; import EmailLogs from "./emailLogs"; export default class EmailSettings extends Component { render() { return ( - -
- -
-
- -
-
+ <> + + ); } } diff --git a/imports/plugins/core/email/client/index.js b/imports/plugins/core/email/client/index.js index 0b1f85eda0..f10b156f5e 100644 --- a/imports/plugins/core/email/client/index.js +++ b/imports/plugins/core/email/client/index.js @@ -1,15 +1,19 @@ -import React from "react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faEnvelope } from "@fortawesome/free-solid-svg-icons"; - import { registerOperatorRoute } from "/imports/client/ui"; +import { registerBlock } from "@reactioncommerce/reaction-components"; +import EmailSettingsRegion from "./components/EmailSettingsRegion"; import EmailSettings from "./containers/EmailSettings"; registerOperatorRoute({ group: "settings", - path: "/email", - MainComponent: EmailSettings, - // eslint-disable-next-line react/display-name - SidebarIconComponent: (props) => , + path: "/settings/email", + MainComponent: EmailSettingsRegion, + priority: 150, sidebarI18nLabel: "admin.dashboard.emailLabel" }); + +registerBlock({ + component: EmailSettings, + name: "EmailSettingsGeneral", + priority: 1, + region: "EmailSettings" +}); diff --git a/imports/plugins/core/i18n/client/components/LocalizationSettingsRegion.js b/imports/plugins/core/i18n/client/components/LocalizationSettingsRegion.js new file mode 100644 index 0000000000..3fcdde2f42 --- /dev/null +++ b/imports/plugins/core/i18n/client/components/LocalizationSettingsRegion.js @@ -0,0 +1,30 @@ +import React from "react"; +import i18next from "i18next"; +import { Blocks } from "@reactioncommerce/reaction-components"; +import { + makeStyles, + Typography +} from "@material-ui/core"; + +const useStyles = makeStyles((theme) => ({ + header: { + marginBottom: theme.spacing(4) + } +})); + +/** + * @summary Renders payment settings page + * @param {Object} props Component props + * @return {React.Node} React node + */ +export default function LocalizationSettingsRegion(props) { + const classes = useStyles(); + return ( + <> + + {i18next.t("admin.settings.localization.header")} + + + + ); +} diff --git a/imports/plugins/core/i18n/client/index.js b/imports/plugins/core/i18n/client/index.js index f109f61304..09584494aa 100644 --- a/imports/plugins/core/i18n/client/index.js +++ b/imports/plugins/core/i18n/client/index.js @@ -1,17 +1,21 @@ -import React from "react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faGlobe } from "@fortawesome/free-solid-svg-icons"; - import { registerOperatorRoute } from "/imports/client/ui"; +import { registerBlock } from "@reactioncommerce/reaction-components"; +import LocalizationSettingsRegion from "./components/LocalizationSettingsRegion"; import Localization from "./containers/localizationSettings"; export { default as LocalizationSettings } from "./containers/localizationSettings"; registerOperatorRoute({ group: "settings", - MainComponent: Localization, - path: "/localization", - // eslint-disable-next-line react/display-name - SidebarIconComponent: (props) => , - sidebarI18nLabel: "admin.i18nSettings.shopLocalization" + MainComponent: LocalizationSettingsRegion, + path: "/settings/localization", + sidebarI18nLabel: "admin.i18nSettings.shopLocalization", + priority: 160 +}); + +registerBlock({ + component: Localization, + name: "LocalizationSettingsGeneral", + priority: 1, + region: "LocalizationSettings" }); diff --git a/imports/plugins/core/inventory/client/InventorySettings.js b/imports/plugins/core/inventory/client/InventorySettings.js index 234dd2f14a..655cf2f96a 100644 --- a/imports/plugins/core/inventory/client/InventorySettings.js +++ b/imports/plugins/core/inventory/client/InventorySettings.js @@ -7,6 +7,14 @@ import CardContent from "@material-ui/core/CardContent"; import CardHeader from "@material-ui/core/CardHeader"; import FormControlLabel from "@material-ui/core/FormControlLabel"; import Switch from "@material-ui/core/Switch"; +import { makeStyles } from "@material-ui/core"; + +const useStyles = makeStyles((theme) => ({ + card: { + marginTop: theme.spacing(3), + marginBottom: theme.spacing(3) + } +})); /** * Inventory settings form block component @@ -20,6 +28,7 @@ function InventorySettings(props) { updateInventoryShopSettings, shopId } = props; + const classes = useStyles(); if (isLoadingInventoryShopSettings) return ; @@ -28,7 +37,7 @@ function InventorySettings(props) { } = inventoryShopSettings || {}; return ( - + - ); } diff --git a/imports/plugins/core/payments/client/PaymentSettingsRegion.js b/imports/plugins/core/payments/client/PaymentSettingsRegion.js new file mode 100644 index 0000000000..4cd2e6ac77 --- /dev/null +++ b/imports/plugins/core/payments/client/PaymentSettingsRegion.js @@ -0,0 +1,30 @@ +import React from "react"; +import i18next from "i18next"; +import { Blocks } from "@reactioncommerce/reaction-components"; +import { + makeStyles, + Typography +} from "@material-ui/core"; + +const useStyles = makeStyles((theme) => ({ + header: { + marginBottom: theme.spacing(4) + } +})); + +/** + * @summary Renders payment settings page + * @param {Object} props Component props + * @return {React.Node} React node + */ +export default function PaymentSettingsRegion(props) { + const classes = useStyles(); + return ( + <> + + {i18next.t("admin.settings.payment.header")} + + + + ); +} diff --git a/imports/plugins/core/payments/client/index.js b/imports/plugins/core/payments/client/index.js index ad8f6c4eea..61727e6abd 100644 --- a/imports/plugins/core/payments/client/index.js +++ b/imports/plugins/core/payments/client/index.js @@ -1,15 +1,19 @@ -import React from "react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faCreditCard } from "@fortawesome/free-solid-svg-icons"; import { registerOperatorRoute } from "/imports/client/ui"; -import PaymentSettings from "./PaymentSettings.js"; +import { registerBlock } from "@reactioncommerce/reaction-components"; +import PaymentSettingsRegion from "./PaymentSettingsRegion"; +import PaymentSettings from "./PaymentSettings"; registerOperatorRoute({ group: "settings", - MainComponent: PaymentSettings, - priority: 20, - path: "/payment", - // eslint-disable-next-line react/display-name - SidebarIconComponent: (props) => , + MainComponent: PaymentSettingsRegion, + priority: 120, + path: "/settings/payment", sidebarI18nLabel: "admin.settings.paymentSettingsLabel" }); + +registerBlock({ + region: "PaymentSettings", + name: "PaymentSettings", + component: PaymentSettings, + priority: 1 +}); diff --git a/imports/plugins/core/shipping/client/components/DefaultParcelSizeForm.js b/imports/plugins/core/shipping/client/components/DefaultParcelSizeForm.js index 6829885579..495fc7c841 100644 --- a/imports/plugins/core/shipping/client/components/DefaultParcelSizeForm.js +++ b/imports/plugins/core/shipping/client/components/DefaultParcelSizeForm.js @@ -8,7 +8,7 @@ import SimpleSchema from "simpl-schema"; import Button from "@reactioncommerce/catalyst/Button"; import Grid from "@material-ui/core/Grid"; import TextField from "@material-ui/core/TextField"; -import { makeStyles } from "@material-ui/styles"; +import { makeStyles } from "@material-ui/core"; import muiOptions from "reacto-form/cjs/muiOptions"; import useReactoForm from "reacto-form/cjs/useReactoForm"; @@ -159,7 +159,7 @@ export default function DefaultParcelSizeForm(props) {