Merge pull request #213 from mirumee/add/language-switch
Add language switch
This commit is contained in:
commit
3eaa3e935c
23 changed files with 327 additions and 49 deletions
|
@ -40,3 +40,4 @@ All notable, unreleased changes to this project will be documented in this file.
|
||||||
- Update sale details design - #207 by @dominik-zeglen
|
- Update sale details design - #207 by @dominik-zeglen
|
||||||
- Improve autocomplete UX - #212 by @dominik-zeglen
|
- Improve autocomplete UX - #212 by @dominik-zeglen
|
||||||
- Fix empty attribute values - #218 by @dominik-zeglen
|
- Fix empty attribute values - #218 by @dominik-zeglen
|
||||||
|
- Add language switch - #213 by @dominik-zeglen
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"POT-Creation-Date: 2019-10-16T15:55:00.250Z\n"
|
"POT-Creation-Date: 2019-10-17T11:58:34.974Z\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
|
@ -59,6 +59,14 @@ msgctxt "service account"
|
||||||
msgid "Account Name"
|
msgid "Account Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/locale/src/components/AppLayout/AppLayout.json
|
||||||
|
#. [src.components.AppLayout.248888005] - button
|
||||||
|
#. defaultMessage is:
|
||||||
|
#. Account Settings
|
||||||
|
msgctxt "button"
|
||||||
|
msgid "Account Settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: build/locale/src/components/AccountStatus/AccountStatus.json
|
#: build/locale/src/components/AccountStatus/AccountStatus.json
|
||||||
#. [src.components.AccountStatus.2183517419] - section header
|
#. [src.components.AccountStatus.2183517419] - section header
|
||||||
#. defaultMessage is:
|
#. defaultMessage is:
|
||||||
|
@ -5755,6 +5763,22 @@ msgctxt "order payment"
|
||||||
msgid "Preauthorized amount"
|
msgid "Preauthorized amount"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/locale/src/staff/components/StaffPreferences/StaffPreferences.json
|
||||||
|
#. [src.staff.components.StaffPreferences.2340309446] - section header
|
||||||
|
#. defaultMessage is:
|
||||||
|
#. Preferences
|
||||||
|
msgctxt "section header"
|
||||||
|
msgid "Preferences"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/locale/src/staff/components/StaffPreferences/StaffPreferences.json
|
||||||
|
#. [src.staff.components.StaffPreferences.2281358617]
|
||||||
|
#. defaultMessage is:
|
||||||
|
#. Preferred Language
|
||||||
|
msgctxt "description"
|
||||||
|
msgid "Preferred Language"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: build/locale/src/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog.json
|
#: build/locale/src/products/components/ProductVariantCreateDialog/ProductVariantCreateDialog.json
|
||||||
#. [src.products.components.ProductVariantCreateDialog.904693740] - previous step, button
|
#. [src.products.components.ProductVariantCreateDialog.904693740] - previous step, button
|
||||||
#. defaultMessage is:
|
#. defaultMessage is:
|
||||||
|
@ -7019,6 +7043,14 @@ msgctxt "description"
|
||||||
msgid "Selected {number} items"
|
msgid "Selected {number} items"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: build/locale/src/staff/components/StaffPreferences/StaffPreferences.json
|
||||||
|
#. [src.staff.components.StaffPreferences.1332294025]
|
||||||
|
#. defaultMessage is:
|
||||||
|
#. Selecting this will change the language of your dashboard
|
||||||
|
msgctxt "description"
|
||||||
|
msgid "Selecting this will change the language of your dashboard"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: build/locale/src/products/components/ProductVariantPrice/ProductVariantPrice.json
|
#: build/locale/src/products/components/ProductVariantPrice/ProductVariantPrice.json
|
||||||
#. [src.products.components.ProductVariantPrice.2238565650]
|
#. [src.products.components.ProductVariantPrice.2238565650]
|
||||||
#. defaultMessage is:
|
#. defaultMessage is:
|
||||||
|
|
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -4953,9 +4953,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"babel-plugin-react-intl-auto": {
|
"babel-plugin-react-intl-auto": {
|
||||||
"version": "2.2.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-react-intl-auto/-/babel-plugin-react-intl-auto-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-plugin-react-intl-auto/-/babel-plugin-react-intl-auto-2.3.0.tgz",
|
||||||
"integrity": "sha512-L6IS4NQCr+uGw8yOJ+tBfm5R0UhrM2mZyhN+X7jCsnEhTcWopkWe7geLm7AzJC2SWFXnr7phwXlJbN4erwKRrA==",
|
"integrity": "sha512-y9QSRHkuN2eAPlvhvvJCbWjxeXhd0kbFLksLkMgaqX7VAiWON04rTSfq4PVbvgfvs5MHdLLbGrxBEWQ129QVIA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/types": "^7.5.5",
|
"@babel/types": "^7.5.5",
|
||||||
|
|
|
@ -104,7 +104,7 @@
|
||||||
"babel-jest": "^23.6.0",
|
"babel-jest": "^23.6.0",
|
||||||
"babel-loader": "^8.0.6",
|
"babel-loader": "^8.0.6",
|
||||||
"babel-plugin-react-intl": "^4.1.12",
|
"babel-plugin-react-intl": "^4.1.12",
|
||||||
"babel-plugin-react-intl-auto": "^2.2.0",
|
"babel-plugin-react-intl-auto": "^2.3.0",
|
||||||
"codecov": "^3.5.0",
|
"codecov": "^3.5.0",
|
||||||
"core-js": "^3.2.1",
|
"core-js": "^3.2.1",
|
||||||
"enzyme": "^3.10.0",
|
"enzyme": "^3.10.0",
|
||||||
|
|
|
@ -29,6 +29,7 @@ import useNavigator from "@saleor/hooks/useNavigator";
|
||||||
import useTheme from "@saleor/hooks/useTheme";
|
import useTheme from "@saleor/hooks/useTheme";
|
||||||
import useUser from "@saleor/hooks/useUser";
|
import useUser from "@saleor/hooks/useUser";
|
||||||
import ArrowDropdown from "@saleor/icons/ArrowDropdown";
|
import ArrowDropdown from "@saleor/icons/ArrowDropdown";
|
||||||
|
import { staffMemberDetailsUrl } from "@saleor/staff/urls";
|
||||||
import Container from "../Container";
|
import Container from "../Container";
|
||||||
import AppActionContext from "./AppActionContext";
|
import AppActionContext from "./AppActionContext";
|
||||||
import AppHeaderContext from "./AppHeaderContext";
|
import AppHeaderContext from "./AppHeaderContext";
|
||||||
|
@ -287,10 +288,15 @@ const AppLayout = withStyles(styles, {
|
||||||
const menuStructure = createMenuStructure(intl);
|
const menuStructure = createMenuStructure(intl);
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
close();
|
setMenuState(false);
|
||||||
logout();
|
logout();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleViewerProfile = () => {
|
||||||
|
setMenuState(false);
|
||||||
|
navigate(staffMemberDetailsUrl(user.id));
|
||||||
|
};
|
||||||
|
|
||||||
const handleMenuItemClick = (
|
const handleMenuItemClick = (
|
||||||
url: string,
|
url: string,
|
||||||
event: React.MouseEvent<any>
|
event: React.MouseEvent<any>
|
||||||
|
@ -434,6 +440,15 @@ const AppLayout = withStyles(styles, {
|
||||||
mouseEvent="onClick"
|
mouseEvent="onClick"
|
||||||
>
|
>
|
||||||
<Menu>
|
<Menu>
|
||||||
|
<MenuItem
|
||||||
|
className={classes.userMenuItem}
|
||||||
|
onClick={handleViewerProfile}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="Account Settings"
|
||||||
|
description="button"
|
||||||
|
/>
|
||||||
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
className={classes.userMenuItem}
|
className={classes.userMenuItem}
|
||||||
onClick={handleLogout}
|
onClick={handleLogout}
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const Date: React.FC<DateProps> = ({ date, plain }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LocaleConsumer>
|
<LocaleConsumer>
|
||||||
{locale => (
|
{({ locale }) => (
|
||||||
<Consumer>
|
<Consumer>
|
||||||
{currentDate =>
|
{currentDate =>
|
||||||
plain ? (
|
plain ? (
|
||||||
|
|
|
@ -27,7 +27,7 @@ export const DateTime: React.StatelessComponent<DateTimeProps> = ({
|
||||||
<TimezoneConsumer>
|
<TimezoneConsumer>
|
||||||
{tz => (
|
{tz => (
|
||||||
<LocaleConsumer>
|
<LocaleConsumer>
|
||||||
{locale => (
|
{({ locale }) => (
|
||||||
<Consumer>
|
<Consumer>
|
||||||
{currentDate =>
|
{currentDate =>
|
||||||
plain ? (
|
plain ? (
|
||||||
|
|
|
@ -42,15 +42,7 @@ import locale_UK from "@locale/uk.json";
|
||||||
import locale_VI from "@locale/vi.json";
|
import locale_VI from "@locale/vi.json";
|
||||||
import locale_ZH_HANS from "@locale/zh-Hans.json";
|
import locale_ZH_HANS from "@locale/zh-Hans.json";
|
||||||
import locale_ZH_HANT from "@locale/zh-Hant.json";
|
import locale_ZH_HANT from "@locale/zh-Hant.json";
|
||||||
|
import useLocalStorage from "@saleor/hooks/useLocalStorage";
|
||||||
const defaultLocale = "en";
|
|
||||||
|
|
||||||
export type LocaleContextType = string;
|
|
||||||
export const LocaleContext = React.createContext<LocaleContextType>(
|
|
||||||
defaultLocale
|
|
||||||
);
|
|
||||||
|
|
||||||
const { Consumer: LocaleConsumer, Provider: RawLocaleProvider } = LocaleContext;
|
|
||||||
|
|
||||||
export enum Locale {
|
export enum Locale {
|
||||||
AR = "ar",
|
AR = "ar",
|
||||||
|
@ -144,6 +136,51 @@ const localeData: Record<Locale, LocaleMessages> = {
|
||||||
[Locale.ZH_HANT]: locale_ZH_HANT
|
[Locale.ZH_HANT]: locale_ZH_HANT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const localeNames: Record<Locale, string> = {
|
||||||
|
[Locale.AR]: "العربيّة",
|
||||||
|
[Locale.AZ]: "Azərbaycanca",
|
||||||
|
[Locale.BG]: "български",
|
||||||
|
[Locale.BN]: "বাংলা",
|
||||||
|
[Locale.CA]: "català",
|
||||||
|
[Locale.CS]: "česky",
|
||||||
|
[Locale.DA]: "dansk",
|
||||||
|
[Locale.DE]: "Deutsch",
|
||||||
|
[Locale.EL]: "Ελληνικά",
|
||||||
|
[Locale.EN]: "English",
|
||||||
|
[Locale.ES]: "español",
|
||||||
|
[Locale.ES_CO]: "español de Colombia",
|
||||||
|
[Locale.ET]: "eesti",
|
||||||
|
[Locale.FA]: "فارسی",
|
||||||
|
[Locale.FR]: "français",
|
||||||
|
[Locale.HI]: "Hindi",
|
||||||
|
[Locale.HU]: "Magyar",
|
||||||
|
[Locale.HY]: "հայերեն",
|
||||||
|
[Locale.ID]: "Bahasa Indonesia",
|
||||||
|
[Locale.IS]: "Íslenska",
|
||||||
|
[Locale.IT]: "italiano",
|
||||||
|
[Locale.JA]: "日本語",
|
||||||
|
[Locale.KO]: "한국어",
|
||||||
|
[Locale.MN]: "Mongolian",
|
||||||
|
[Locale.NB]: "norsk (bokmål)",
|
||||||
|
[Locale.NL]: "Nederlands",
|
||||||
|
[Locale.PL]: "polski",
|
||||||
|
[Locale.PT]: "Português",
|
||||||
|
[Locale.PT_BR]: "Português Brasileiro",
|
||||||
|
[Locale.RO]: "Română",
|
||||||
|
[Locale.RU]: "Русский",
|
||||||
|
[Locale.SK]: "Slovensky",
|
||||||
|
[Locale.SL]: "Slovenščina",
|
||||||
|
[Locale.SQ]: "shqip",
|
||||||
|
[Locale.SR]: "српски",
|
||||||
|
[Locale.SV]: "svenska",
|
||||||
|
[Locale.TH]: "ภาษาไทย",
|
||||||
|
[Locale.TR]: "Türkçe",
|
||||||
|
[Locale.UK]: "Українська",
|
||||||
|
[Locale.VI]: "Tiếng Việt",
|
||||||
|
[Locale.ZH_HANS]: "简体中文",
|
||||||
|
[Locale.ZH_HANT]: "繁體中文"
|
||||||
|
};
|
||||||
|
|
||||||
export function getMatchingLocale(languages: readonly string[]): Locale {
|
export function getMatchingLocale(languages: readonly string[]): Locale {
|
||||||
const localeEntries = Object.entries(Locale);
|
const localeEntries = Object.entries(Locale);
|
||||||
|
|
||||||
|
@ -158,8 +195,22 @@ export function getMatchingLocale(languages: readonly string[]): Locale {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultLocale = Locale.EN;
|
||||||
|
|
||||||
|
export interface LocaleContextType {
|
||||||
|
locale: Locale;
|
||||||
|
setLocale: (locale: Locale) => void;
|
||||||
|
}
|
||||||
|
export const LocaleContext = React.createContext<LocaleContextType>({
|
||||||
|
locale: defaultLocale,
|
||||||
|
setLocale: () => undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
const { Consumer: LocaleConsumer, Provider: RawLocaleProvider } = LocaleContext;
|
||||||
|
|
||||||
const LocaleProvider: React.FC = ({ children }) => {
|
const LocaleProvider: React.FC = ({ children }) => {
|
||||||
const [locale] = React.useState(
|
const [locale, setLocale] = useLocalStorage(
|
||||||
|
"locale",
|
||||||
getMatchingLocale(navigator.languages) || defaultLocale
|
getMatchingLocale(navigator.languages) || defaultLocale
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -175,7 +226,14 @@ const LocaleProvider: React.FC = ({ children }) => {
|
||||||
}}
|
}}
|
||||||
key={locale}
|
key={locale}
|
||||||
>
|
>
|
||||||
<RawLocaleProvider value={locale}>{children}</RawLocaleProvider>
|
<RawLocaleProvider
|
||||||
|
value={{
|
||||||
|
locale,
|
||||||
|
setLocale
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</RawLocaleProvider>
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,7 @@ export interface MoneyProps {
|
||||||
|
|
||||||
export const Money: React.StatelessComponent<MoneyProps> = ({ money }) => (
|
export const Money: React.StatelessComponent<MoneyProps> = ({ money }) => (
|
||||||
<LocaleConsumer>
|
<LocaleConsumer>
|
||||||
{locale => {
|
{({ locale }) => {
|
||||||
return money.amount.toLocaleString(locale, {
|
return money.amount.toLocaleString(locale, {
|
||||||
currency: money.currency,
|
currency: money.currency,
|
||||||
style: "currency"
|
style: "currency"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import { LocaleConsumer } from "../Locale";
|
import { LocaleConsumer } from "../Locale";
|
||||||
import IMoney from "../Money";
|
import IMoney from "../Money";
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ export const MoneyRange: React.StatelessComponent<MoneyRangeProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LocaleConsumer>
|
<LocaleConsumer>
|
||||||
{locale =>
|
{({ locale }) =>
|
||||||
from && to
|
from && to
|
||||||
? intl.formatMessage(
|
? intl.formatMessage(
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,7 +8,7 @@ interface PercentProps {
|
||||||
|
|
||||||
const Percent: React.StatelessComponent<PercentProps> = ({ amount }) => (
|
const Percent: React.StatelessComponent<PercentProps> = ({ amount }) => (
|
||||||
<LocaleConsumer>
|
<LocaleConsumer>
|
||||||
{locale => {
|
{({ locale }) => {
|
||||||
return (amount / 100).toLocaleString(locale, {
|
return (amount / 100).toLocaleString(locale, {
|
||||||
maximumFractionDigits: 2,
|
maximumFractionDigits: 2,
|
||||||
style: "percent"
|
style: "percent"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useContext } from "react";
|
||||||
import { LocaleContext } from "@saleor/components/Locale";
|
import { LocaleContext } from "@saleor/components/Locale";
|
||||||
|
|
||||||
function useDateLocalize(): (date: string) => string {
|
function useDateLocalize(): (date: string) => string {
|
||||||
const locale = useContext(LocaleContext);
|
const { locale } = useContext(LocaleContext);
|
||||||
|
|
||||||
return (date: string) =>
|
return (date: string) =>
|
||||||
moment(date)
|
moment(date)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { useContext } from "react";
|
||||||
import { LocaleContext } from "@saleor/components/Locale";
|
import { LocaleContext } from "@saleor/components/Locale";
|
||||||
|
|
||||||
function useLocale() {
|
function useLocale() {
|
||||||
const themeInfo = useContext(LocaleContext);
|
const localeInfo = useContext(LocaleContext);
|
||||||
return themeInfo;
|
return localeInfo;
|
||||||
}
|
}
|
||||||
export default useLocale;
|
export default useLocale;
|
||||||
|
|
|
@ -332,3 +332,7 @@ export function parseBoolean(a: string): boolean {
|
||||||
}
|
}
|
||||||
return a === "true";
|
return a === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function capitalize(s: string) {
|
||||||
|
return s.charAt(0).toLocaleUpperCase() + s.slice(1);
|
||||||
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ interface ProductListProps {
|
||||||
export const ProductList: React.StatelessComponent<ProductListProps> = ({
|
export const ProductList: React.StatelessComponent<ProductListProps> = ({
|
||||||
params
|
params
|
||||||
}) => {
|
}) => {
|
||||||
const locale = useLocale();
|
const { locale } = useLocale();
|
||||||
const navigate = useNavigator();
|
const navigate = useNavigator();
|
||||||
const notify = useNotifier();
|
const notify = useNotifier();
|
||||||
const paginate = usePaginator();
|
const paginate = usePaginator();
|
||||||
|
|
|
@ -12,10 +12,12 @@ import Grid from "@saleor/components/Grid";
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||||
import { ShopInfo_shop_permissions } from "@saleor/components/Shop/types/ShopInfo";
|
import { ShopInfo_shop_permissions } from "@saleor/components/Shop/types/ShopInfo";
|
||||||
|
import useLocale from "@saleor/hooks/useLocale";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { getUserName, maybe } from "../../../misc";
|
import { getUserName, maybe } from "../../../misc";
|
||||||
import { PermissionEnum } from "../../../types/globalTypes";
|
import { PermissionEnum } from "../../../types/globalTypes";
|
||||||
import { StaffMemberDetails_user } from "../../types/StaffMemberDetails";
|
import { StaffMemberDetails_user } from "../../types/StaffMemberDetails";
|
||||||
|
import StaffPreferences from "../StaffPreferences";
|
||||||
import StaffProperties from "../StaffProperties/StaffProperties";
|
import StaffProperties from "../StaffProperties/StaffProperties";
|
||||||
|
|
||||||
interface FormData {
|
interface FormData {
|
||||||
|
@ -29,6 +31,7 @@ interface FormData {
|
||||||
|
|
||||||
export interface StaffDetailsPageProps {
|
export interface StaffDetailsPageProps {
|
||||||
canEditAvatar: boolean;
|
canEditAvatar: boolean;
|
||||||
|
canEditPreferences: boolean;
|
||||||
canEditStatus: boolean;
|
canEditStatus: boolean;
|
||||||
canRemove: boolean;
|
canRemove: boolean;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
@ -44,6 +47,7 @@ export interface StaffDetailsPageProps {
|
||||||
|
|
||||||
const StaffDetailsPage: React.StatelessComponent<StaffDetailsPageProps> = ({
|
const StaffDetailsPage: React.StatelessComponent<StaffDetailsPageProps> = ({
|
||||||
canEditAvatar,
|
canEditAvatar,
|
||||||
|
canEditPreferences,
|
||||||
canEditStatus,
|
canEditStatus,
|
||||||
canRemove,
|
canRemove,
|
||||||
disabled,
|
disabled,
|
||||||
|
@ -57,6 +61,7 @@ const StaffDetailsPage: React.StatelessComponent<StaffDetailsPageProps> = ({
|
||||||
onSubmit
|
onSubmit
|
||||||
}: StaffDetailsPageProps) => {
|
}: StaffDetailsPageProps) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
const { locale, setLocale } = useLocale();
|
||||||
|
|
||||||
const initialForm: FormData = {
|
const initialForm: FormData = {
|
||||||
email: maybe(() => staffMember.email, ""),
|
email: maybe(() => staffMember.email, ""),
|
||||||
|
@ -75,6 +80,7 @@ const StaffDetailsPage: React.StatelessComponent<StaffDetailsPageProps> = ({
|
||||||
lastName: maybe(() => staffMember.lastName, ""),
|
lastName: maybe(() => staffMember.lastName, ""),
|
||||||
permissions: maybe(() => staffMember.permissions, []).map(perm => perm.code)
|
permissions: maybe(() => staffMember.permissions, []).map(perm => perm.code)
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form initial={initialForm} onSubmit={onSubmit} confirmLeave>
|
<Form initial={initialForm} onSubmit={onSubmit} confirmLeave>
|
||||||
{({ data, change, hasChanged, submit }) => (
|
{({ data, change, hasChanged, submit }) => (
|
||||||
|
@ -95,26 +101,31 @@ const StaffDetailsPage: React.StatelessComponent<StaffDetailsPageProps> = ({
|
||||||
onImageDelete={onImageDelete}
|
onImageDelete={onImageDelete}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{canEditStatus && (
|
<div>
|
||||||
<div>
|
{canEditPreferences && (
|
||||||
<AccountPermissions
|
<StaffPreferences locale={locale} onLocaleChange={setLocale} />
|
||||||
data={data}
|
)}
|
||||||
disabled={disabled}
|
{canEditStatus && (
|
||||||
permissions={permissions}
|
<>
|
||||||
onChange={change}
|
<AccountPermissions
|
||||||
/>
|
data={data}
|
||||||
<CardSpacer />
|
disabled={disabled}
|
||||||
<AccountStatus
|
permissions={permissions}
|
||||||
data={data}
|
onChange={change}
|
||||||
disabled={disabled}
|
/>
|
||||||
label={intl.formatMessage({
|
<CardSpacer />
|
||||||
defaultMessage: "User is active",
|
<AccountStatus
|
||||||
description: "checkbox label"
|
data={data}
|
||||||
})}
|
disabled={disabled}
|
||||||
onChange={change}
|
label={intl.formatMessage({
|
||||||
/>
|
defaultMessage: "User is active",
|
||||||
</div>
|
description: "checkbox label"
|
||||||
)}
|
})}
|
||||||
|
onChange={change}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</Grid>
|
</Grid>
|
||||||
<SaveButtonBar
|
<SaveButtonBar
|
||||||
disabled={disabled || !hasChanged}
|
disabled={disabled || !hasChanged}
|
||||||
|
|
53
src/staff/components/StaffPreferences/StaffPreferences.tsx
Normal file
53
src/staff/components/StaffPreferences/StaffPreferences.tsx
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import Card from "@material-ui/core/Card";
|
||||||
|
import CardContent from "@material-ui/core/CardContent";
|
||||||
|
import React from "react";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
|
import { Locale, localeNames } from "@saleor/components/Locale";
|
||||||
|
import SingleAutocompleteSelectField from "@saleor/components/SingleAutocompleteSelectField";
|
||||||
|
import { capitalize } from "@saleor/misc";
|
||||||
|
|
||||||
|
interface StaffPreferencesProps {
|
||||||
|
locale: Locale;
|
||||||
|
onLocaleChange: (locale: Locale) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StaffPreferences: React.StatelessComponent<StaffPreferencesProps> = ({
|
||||||
|
locale,
|
||||||
|
onLocaleChange
|
||||||
|
}) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardTitle
|
||||||
|
title={intl.formatMessage({
|
||||||
|
defaultMessage: "Preferences",
|
||||||
|
description: "section header"
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<CardContent>
|
||||||
|
<SingleAutocompleteSelectField
|
||||||
|
choices={Object.values(Locale).map(locale => ({
|
||||||
|
label: capitalize(localeNames[locale]),
|
||||||
|
value: locale
|
||||||
|
}))}
|
||||||
|
displayValue={localeNames[locale]}
|
||||||
|
helperText={intl.formatMessage({
|
||||||
|
defaultMessage:
|
||||||
|
"Selecting this will change the language of your dashboard"
|
||||||
|
})}
|
||||||
|
label={intl.formatMessage({
|
||||||
|
defaultMessage: "Preferred Language"
|
||||||
|
})}
|
||||||
|
name="locale"
|
||||||
|
value={locale}
|
||||||
|
onChange={event => onLocaleChange(event.target.value)}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
StaffPreferences.displayName = "StaffPreferences";
|
||||||
|
export default StaffPreferences;
|
2
src/staff/components/StaffPreferences/index.ts
Normal file
2
src/staff/components/StaffPreferences/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from "./StaffPreferences";
|
||||||
|
export * from "./StaffPreferences";
|
|
@ -126,6 +126,7 @@ export const StaffDetails: React.StatelessComponent<OrderListProps> = ({
|
||||||
/>
|
/>
|
||||||
<StaffDetailsPage
|
<StaffDetailsPage
|
||||||
canEditAvatar={isUserSameAsViewer}
|
canEditAvatar={isUserSameAsViewer}
|
||||||
|
canEditPreferences={isUserSameAsViewer}
|
||||||
canEditStatus={!isUserSameAsViewer}
|
canEditStatus={!isUserSameAsViewer}
|
||||||
canRemove={!isUserSameAsViewer}
|
canRemove={!isUserSameAsViewer}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { IntlProvider } from "react-intl";
|
import { IntlProvider } from "react-intl";
|
||||||
|
|
||||||
import { RawLocaleProvider } from "@saleor/components/Locale";
|
import { Locale, RawLocaleProvider } from "@saleor/components/Locale";
|
||||||
import { Provider as DateProvider } from "../components/Date/DateContext";
|
import { Provider as DateProvider } from "../components/Date/DateContext";
|
||||||
import { MessageManager } from "../components/messages";
|
import { MessageManager } from "../components/messages";
|
||||||
import ThemeProvider from "../components/Theme";
|
import ThemeProvider from "../components/Theme";
|
||||||
import { TimezoneProvider } from "../components/Timezone";
|
import { TimezoneProvider } from "../components/Timezone";
|
||||||
|
|
||||||
export const Decorator = storyFn => (
|
export const Decorator = storyFn => (
|
||||||
<IntlProvider defaultLocale="en" locale="en">
|
<IntlProvider defaultLocale={Locale.EN} locale={Locale.EN}>
|
||||||
<RawLocaleProvider value="en">
|
<RawLocaleProvider
|
||||||
|
value={{
|
||||||
|
locale: Locale.EN,
|
||||||
|
setLocale: () => undefined
|
||||||
|
}}
|
||||||
|
>
|
||||||
<DateProvider value={+new Date("2018-08-07T14:30:44+00:00")}>
|
<DateProvider value={+new Date("2018-08-07T14:30:44+00:00")}>
|
||||||
<TimezoneProvider value="America/New_York">
|
<TimezoneProvider value="America/New_York">
|
||||||
<ThemeProvider isDefaultDark={false}>
|
<ThemeProvider isDefaultDark={false}>
|
||||||
|
|
|
@ -118801,6 +118801,98 @@ exports[`Storyshots Views / Staff / Staff member details himself 1`] = `
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="MuiPaper-root-id MuiPaper-elevation0-id MuiPaper-rounded-id MuiCard-root-id"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="CardTitle-root-id"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="MuiTypography-root-id MuiTypography-h5-id CardTitle-title-id"
|
||||||
|
>
|
||||||
|
Preferences
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
class="CardTitle-toolbar-id"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="CardTitle-children-id"
|
||||||
|
/>
|
||||||
|
<hr
|
||||||
|
class="CardTitle-hr-id"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="MuiCardContent-root-id"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="SingleAutocompleteSelectField-container-id"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="MuiFormControl-root-id MuiFormControl-fullWidth-id"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="MuiFormLabel-root-id MuiFormLabel-filled-id MuiInputLabel-root-id MuiInputLabel-formControl-id MuiInputLabel-animated-id MuiInputLabel-shrink-id MuiInputLabel-outlined-id"
|
||||||
|
data-shrink="true"
|
||||||
|
>
|
||||||
|
Preferred Language
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
aria-autocomplete="list"
|
||||||
|
aria-expanded="false"
|
||||||
|
class="MuiInputBase-root-id MuiOutlinedInput-root-id MuiInputBase-fullWidth-id MuiInputBase-formControl-id MuiInputBase-adornedEnd-id MuiOutlinedInput-adornedEnd-id"
|
||||||
|
role="combobox"
|
||||||
|
>
|
||||||
|
<fieldset
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiPrivateNotchedOutline-root-id MuiOutlinedInput-notchedOutline-id"
|
||||||
|
style="padding-left:8px"
|
||||||
|
>
|
||||||
|
<legend
|
||||||
|
class="MuiPrivateNotchedOutline-legend-id"
|
||||||
|
style="width:0"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
|
||||||
|
</span>
|
||||||
|
</legend>
|
||||||
|
</fieldset>
|
||||||
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
|
autocomplete="off"
|
||||||
|
class="MuiInputBase-input-id MuiOutlinedInput-input-id MuiInputBase-inputAdornedEnd-id MuiOutlinedInput-inputAdornedEnd-id"
|
||||||
|
type="text"
|
||||||
|
value="English"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="MuiSvgIcon-root-id"
|
||||||
|
focusable="false"
|
||||||
|
role="presentation"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
style="fill-rule:evenodd"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M7 10l5 5 5-5z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="MuiFormHelperText-root-id MuiFormHelperText-contained-id MuiFormHelperText-filled-id"
|
||||||
|
>
|
||||||
|
Selecting this will change the language of your dashboard
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -11,6 +11,7 @@ import Decorator from "../../Decorator";
|
||||||
|
|
||||||
const props: Omit<StaffDetailsPageProps, "classes"> = {
|
const props: Omit<StaffDetailsPageProps, "classes"> = {
|
||||||
canEditAvatar: false,
|
canEditAvatar: false,
|
||||||
|
canEditPreferences: false,
|
||||||
canEditStatus: true,
|
canEditStatus: true,
|
||||||
canRemove: true,
|
canRemove: true,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
@ -45,5 +46,6 @@ storiesOf("Views / Staff / Staff member details", module)
|
||||||
canEditStatus={false}
|
canEditStatus={false}
|
||||||
canRemove={false}
|
canRemove={false}
|
||||||
canEditAvatar={true}
|
canEditAvatar={true}
|
||||||
|
canEditPreferences={true}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
|
@ -526,6 +526,7 @@ export default (colors: IThemeColors): Theme =>
|
||||||
body2: {
|
body2: {
|
||||||
fontSize: "1rem"
|
fontSize: "1rem"
|
||||||
},
|
},
|
||||||
|
fontFamily,
|
||||||
h4: {
|
h4: {
|
||||||
color: colors.font.default
|
color: colors.font.default
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue