diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b34dbe1c99..d7d44250393 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ All notable, unreleased changes to this project will be documented in this file. - Remove unused decorator - #4036 by @maarcingebala - Overall improvement of the GraphQL performances, especially on single nodes - #3968 @NyanKiyoshi - Remove unnecessary dedents from GraphQL schema so new Playground can work - #4045 by @salwator +- Add user avatar management - #4030 by @benekex2 ## 2.5.0 diff --git a/package.json b/package.json index 74827c01ec4..449af4ecd35 100644 --- a/package.json +++ b/package.json @@ -199,6 +199,7 @@ "generate-component": "plop --plopfile .plop/plopfile.js", "heroku-postbuild": "npm run build-assets && npm run build-emails", "lint": "tslint 'saleor/static/dashboard-next/**/*.{ts,tsx}'", + "lint-fix": "tslint 'saleor/static/dashboard-next/**/*.{ts,tsx}' --fix", "start": "webpack -d --watch", "build-emails": "mjml --config.beautify false -l skip \"templates/templated_email/source/*.mjml\" -o templates/templated_email/compiled", "storybook": "start-storybook -p 3000 -c saleor/static/dashboard-next/storybook/", diff --git a/saleor/static/dashboard-next/categories/fixtures.ts b/saleor/static/dashboard-next/categories/fixtures.ts index a865375303c..9fc71ce5c32 100644 --- a/saleor/static/dashboard-next/categories/fixtures.ts +++ b/saleor/static/dashboard-next/categories/fixtures.ts @@ -92,7 +92,7 @@ export const category: ( node: { __typename: "Product", availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: true }, id: "UHJvZHVjdDoyMQ==", @@ -116,7 +116,7 @@ export const category: ( node: { __typename: "Product", availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: true }, id: "UHJvZHVjdDoyMg==", @@ -140,7 +140,7 @@ export const category: ( node: { __typename: "Product", availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: true }, id: "UHJvZHVjdDoyMw==", @@ -164,7 +164,7 @@ export const category: ( node: { __typename: "Product", availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: true }, id: "UHJvZHVjdDoyNA==", @@ -188,7 +188,7 @@ export const category: ( node: { __typename: "Product", availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: true }, id: "UHJvZHVjdDoyNQ==", @@ -212,7 +212,7 @@ export const category: ( node: { __typename: "Product", availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: true }, id: "UHJvZHVjdDoyNg==", @@ -236,7 +236,7 @@ export const category: ( node: { __typename: "Product", availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: true }, id: "UHJvZHVjdDoyNw==", @@ -260,7 +260,7 @@ export const category: ( node: { __typename: "Product", availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: true }, id: "UHJvZHVjdDoyOA==", @@ -284,7 +284,7 @@ export const category: ( node: { __typename: "Product", availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: true }, id: "UHJvZHVjdDoyOQ==", @@ -308,7 +308,7 @@ export const category: ( node: { __typename: "Product", availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: true }, id: "UHJvZHVjdDozMA==", diff --git a/saleor/static/dashboard-next/categories/types/CategoryDetails.ts b/saleor/static/dashboard-next/categories/types/CategoryDetails.ts index dfd03d48f92..101a84c3ac0 100644 --- a/saleor/static/dashboard-next/categories/types/CategoryDetails.ts +++ b/saleor/static/dashboard-next/categories/types/CategoryDetails.ts @@ -54,7 +54,7 @@ export interface CategoryDetails_category_products_pageInfo { } export interface CategoryDetails_category_products_edges_node_availability { - __typename: "ProductAvailability"; + __typename: "ProductPricingInfo"; available: boolean | null; } diff --git a/saleor/static/dashboard-next/products/fixtures.ts b/saleor/static/dashboard-next/products/fixtures.ts index 2484fda1867..016b1868a61 100644 --- a/saleor/static/dashboard-next/products/fixtures.ts +++ b/saleor/static/dashboard-next/products/fixtures.ts @@ -91,7 +91,7 @@ export const product: ( } ], availability: { - __typename: "ProductAvailability", + __typename: "ProductPricingInfo", available: false, priceRange: { __typename: "TaxedMoneyRange", diff --git a/saleor/static/dashboard-next/products/types/Product.ts b/saleor/static/dashboard-next/products/types/Product.ts index 10ebd1f2ce0..9797b91e990 100644 --- a/saleor/static/dashboard-next/products/types/Product.ts +++ b/saleor/static/dashboard-next/products/types/Product.ts @@ -104,7 +104,7 @@ export interface Product_availability_priceRange { } export interface Product_availability { - __typename: "ProductAvailability"; + __typename: "ProductPricingInfo"; available: boolean | null; priceRange: Product_availability_priceRange | null; } diff --git a/saleor/static/dashboard-next/products/types/ProductCreate.ts b/saleor/static/dashboard-next/products/types/ProductCreate.ts index d4443194300..a8e9bb0283b 100644 --- a/saleor/static/dashboard-next/products/types/ProductCreate.ts +++ b/saleor/static/dashboard-next/products/types/ProductCreate.ts @@ -112,7 +112,7 @@ export interface ProductCreate_productCreate_product_availability_priceRange { } export interface ProductCreate_productCreate_product_availability { - __typename: "ProductAvailability"; + __typename: "ProductPricingInfo"; available: boolean | null; priceRange: ProductCreate_productCreate_product_availability_priceRange | null; } diff --git a/saleor/static/dashboard-next/products/types/ProductDetails.ts b/saleor/static/dashboard-next/products/types/ProductDetails.ts index 2a3e64f5211..1623b5985a2 100644 --- a/saleor/static/dashboard-next/products/types/ProductDetails.ts +++ b/saleor/static/dashboard-next/products/types/ProductDetails.ts @@ -104,7 +104,7 @@ export interface ProductDetails_product_availability_priceRange { } export interface ProductDetails_product_availability { - __typename: "ProductAvailability"; + __typename: "ProductPricingInfo"; available: boolean | null; priceRange: ProductDetails_product_availability_priceRange | null; } diff --git a/saleor/static/dashboard-next/products/types/ProductFragment.ts b/saleor/static/dashboard-next/products/types/ProductFragment.ts index 6b1f88316bf..b831bfed3ce 100644 --- a/saleor/static/dashboard-next/products/types/ProductFragment.ts +++ b/saleor/static/dashboard-next/products/types/ProductFragment.ts @@ -12,7 +12,7 @@ export interface ProductFragment_thumbnail { } export interface ProductFragment_availability { - __typename: "ProductAvailability"; + __typename: "ProductPricingInfo"; available: boolean | null; } diff --git a/saleor/static/dashboard-next/products/types/ProductImageCreate.ts b/saleor/static/dashboard-next/products/types/ProductImageCreate.ts index 90cc2fe767d..f751ed83f51 100644 --- a/saleor/static/dashboard-next/products/types/ProductImageCreate.ts +++ b/saleor/static/dashboard-next/products/types/ProductImageCreate.ts @@ -110,7 +110,7 @@ export interface ProductImageCreate_productImageCreate_product_availability_pric } export interface ProductImageCreate_productImageCreate_product_availability { - __typename: "ProductAvailability"; + __typename: "ProductPricingInfo"; available: boolean | null; priceRange: ProductImageCreate_productImageCreate_product_availability_priceRange | null; } diff --git a/saleor/static/dashboard-next/products/types/ProductImageUpdate.ts b/saleor/static/dashboard-next/products/types/ProductImageUpdate.ts index b05ef90ae4f..734eaf0fdd8 100644 --- a/saleor/static/dashboard-next/products/types/ProductImageUpdate.ts +++ b/saleor/static/dashboard-next/products/types/ProductImageUpdate.ts @@ -110,7 +110,7 @@ export interface ProductImageUpdate_productImageUpdate_product_availability_pric } export interface ProductImageUpdate_productImageUpdate_product_availability { - __typename: "ProductAvailability"; + __typename: "ProductPricingInfo"; available: boolean | null; priceRange: ProductImageUpdate_productImageUpdate_product_availability_priceRange | null; } diff --git a/saleor/static/dashboard-next/products/types/ProductList.ts b/saleor/static/dashboard-next/products/types/ProductList.ts index 4ccbd10f8ee..cfb7cd582a0 100644 --- a/saleor/static/dashboard-next/products/types/ProductList.ts +++ b/saleor/static/dashboard-next/products/types/ProductList.ts @@ -14,7 +14,7 @@ export interface ProductList_products_edges_node_thumbnail { } export interface ProductList_products_edges_node_availability { - __typename: "ProductAvailability"; + __typename: "ProductPricingInfo"; available: boolean | null; } diff --git a/saleor/static/dashboard-next/products/types/ProductUpdate.ts b/saleor/static/dashboard-next/products/types/ProductUpdate.ts index d49b7c077a5..615a089ef4f 100644 --- a/saleor/static/dashboard-next/products/types/ProductUpdate.ts +++ b/saleor/static/dashboard-next/products/types/ProductUpdate.ts @@ -112,7 +112,7 @@ export interface ProductUpdate_productUpdate_product_availability_priceRange { } export interface ProductUpdate_productUpdate_product_availability { - __typename: "ProductAvailability"; + __typename: "ProductPricingInfo"; available: boolean | null; priceRange: ProductUpdate_productUpdate_product_availability_priceRange | null; } diff --git a/saleor/static/dashboard-next/products/types/SimpleProductUpdate.ts b/saleor/static/dashboard-next/products/types/SimpleProductUpdate.ts index fd9c816e2b0..99e9d6e5f49 100644 --- a/saleor/static/dashboard-next/products/types/SimpleProductUpdate.ts +++ b/saleor/static/dashboard-next/products/types/SimpleProductUpdate.ts @@ -112,7 +112,7 @@ export interface SimpleProductUpdate_productUpdate_product_availability_priceRan } export interface SimpleProductUpdate_productUpdate_product_availability { - __typename: "ProductAvailability"; + __typename: "ProductPricingInfo"; available: boolean | null; priceRange: SimpleProductUpdate_productUpdate_product_availability_priceRange | null; } diff --git a/saleor/static/dashboard-next/staff/components/StaffDetailsPage/StaffDetailsPage.tsx b/saleor/static/dashboard-next/staff/components/StaffDetailsPage/StaffDetailsPage.tsx index 822315c09db..2c8be02f99e 100644 --- a/saleor/static/dashboard-next/staff/components/StaffDetailsPage/StaffDetailsPage.tsx +++ b/saleor/static/dashboard-next/staff/components/StaffDetailsPage/StaffDetailsPage.tsx @@ -29,6 +29,7 @@ interface FormData { } export interface StaffDetailsPageProps { + canEditAvatar: boolean; canEditStatus: boolean; canRemove: boolean; disabled: boolean; @@ -37,10 +38,13 @@ export interface StaffDetailsPageProps { staffMember: StaffMemberDetails_user; onBack: () => void; onDelete: () => void; + onImageDelete: () => void; onSubmit: (data: FormData) => void; + onImageUpload(file: File); } const StaffDetailsPage: React.StatelessComponent = ({ + canEditAvatar, canEditStatus, canRemove, disabled, @@ -49,6 +53,8 @@ const StaffDetailsPage: React.StatelessComponent = ({ staffMember, onBack, onDelete, + onImageDelete, + onImageUpload, onSubmit }: StaffDetailsPageProps) => { const initialForm: FormData = { @@ -79,8 +85,11 @@ const StaffDetailsPage: React.StatelessComponent = ({ {canEditStatus && ( diff --git a/saleor/static/dashboard-next/staff/components/StaffList/StaffList.tsx b/saleor/static/dashboard-next/staff/components/StaffList/StaffList.tsx index e4658eaeaac..8cfc11b86ba 100644 --- a/saleor/static/dashboard-next/staff/components/StaffList/StaffList.tsx +++ b/saleor/static/dashboard-next/staff/components/StaffList/StaffList.tsx @@ -18,7 +18,12 @@ import * as React from "react"; import Skeleton from "../../../components/Skeleton"; import TablePagination from "../../../components/TablePagination"; import i18n from "../../../i18n"; -import { getUserName, maybe, renderCollection } from "../../../misc"; +import { + getUserInitials, + getUserName, + maybe, + renderCollection +} from "../../../misc"; import { ListProps } from "../../../types"; import { StaffList_staffUsers_edges_node } from "../../types/StaffList"; @@ -32,7 +37,18 @@ const styles = (theme: Theme) => height: 47, justifyContent: "center", marginRight: theme.spacing.unit * 1 + "px", - width: 37 + overflow: "hidden", + width: 47 + }, + avatarDefault: { + "& p": { + color: "#fff", + lineHeight: "47px" + }, + background: theme.palette.primary.main, + height: 47, + textAlign: "center", + width: 47 }, avatarImage: { pointerEvents: "none", @@ -104,10 +120,16 @@ const StaffList = withStyles(styles, { name: "StaffList" })( >
- staffMember.avatar.url)} - /> + {maybe(() => staffMember.avatar.url) ? ( + staffMember.avatar.url)} + /> + ) : ( +
+ {getUserInitials(staffMember)} +
+ )}
{getUserName(staffMember) || } diff --git a/saleor/static/dashboard-next/staff/components/StaffProperties/StaffProperties.tsx b/saleor/static/dashboard-next/staff/components/StaffProperties/StaffProperties.tsx index 2ab88f3d2e2..82661588265 100644 --- a/saleor/static/dashboard-next/staff/components/StaffProperties/StaffProperties.tsx +++ b/saleor/static/dashboard-next/staff/components/StaffProperties/StaffProperties.tsx @@ -7,27 +7,75 @@ import { WithStyles } from "@material-ui/core/styles"; import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; import * as React from "react"; +import SVG from "react-inlinesvg"; import CardTitle from "../../../components/CardTitle"; import i18n from "../../../i18n"; -import { maybe } from "../../../misc"; +import { getUserInitials, maybe } from "../../../misc"; import { StaffMemberDetails_user } from "../../types/StaffMemberDetails"; +import * as photoIcon from "../../../../images/photo-icon.svg"; + const styles = (theme: Theme) => createStyles({ avatar: { + "& svg": { + fill: "#fff" + }, + "&:hover $avatarHover": { + opacity: 1 + }, alignItems: "center", borderRadius: "100%", display: "grid", height: 120, justifyContent: "center", + overflow: "hidden", + position: "relative", + width: 120 + }, + avatarDefault: { + "& p": { + color: "#fff", + fontSize: 35, + fontWeight: "bold", + lineHeight: "120px" + }, + background: theme.palette.primary.main, + height: 120, + textAlign: "center", + width: 120 + }, + avatarHover: { + "& p": { + "&:hover": { + textDecoration: "underline" + }, + color: theme.palette.primary.main, + cursor: "pointer", + fontSize: 12, + fontWeight: 500 + }, + background: "#00000080", + borderRadius: "100%", + height: 120, + opacity: 0, + padding: `${theme.spacing.unit * 2.5}px 0`, + position: "absolute", + textAlign: "center", + textTransform: "uppercase", + transition: "opacity 0.5s", width: 120 }, avatarImage: { pointerEvents: "none", width: "100%" }, + fileField: { + display: "none" + }, prop: { marginBottom: theme.spacing.unit * 2 + "px" }, @@ -48,6 +96,7 @@ const styles = (theme: Theme) => }); interface StaffPropertiesProps extends WithStyles { + canEditAvatar: boolean; className?: string; data: { email: string; @@ -57,63 +106,96 @@ interface StaffPropertiesProps extends WithStyles { disabled: boolean; staffMember: StaffMemberDetails_user; onChange: (event: React.ChangeEvent) => void; + onImageDelete: () => void; + onImageUpload: (file: File) => void; } const StaffProperties = withStyles(styles, { name: "StaffProperties" })( ({ + canEditAvatar, classes, className, data, staffMember, - onChange - }: StaffPropertiesProps) => ( - - - -
-
-
- staffMember.avatar.url)} - /> -
-
-
-
-
- -
-
- + onChange, + onImageDelete, + onImageUpload + }: StaffPropertiesProps) => { + const imgInputAnchor = React.createRef(); + const clickImgInput = () => imgInputAnchor.current.click(); + return ( + + + +
+
+
+ {maybe(() => staffMember.avatar.url) ? ( + staffMember.avatar.url)} + /> + ) : ( +
+ {getUserInitials(data)} +
+ )} + {canEditAvatar && ( +
+ + + {i18n.t("Change photo")} + + + {i18n.t("Delete photo")} + + onImageUpload(event.target.files[0])} + type="file" + ref={imgInputAnchor} + /> +
+ )}
-
- +
+
+
+
+ +
+
+ +
+
+ +
-
-
-
- ) + + + ); + } ); StaffProperties.displayName = "StaffProperties"; export default StaffProperties; diff --git a/saleor/static/dashboard-next/staff/mutations.ts b/saleor/static/dashboard-next/staff/mutations.ts index 4f2d352e81a..a3939ecc35d 100644 --- a/saleor/static/dashboard-next/staff/mutations.ts +++ b/saleor/static/dashboard-next/staff/mutations.ts @@ -2,6 +2,11 @@ import gql from "graphql-tag"; import { TypedMutation } from "../mutations"; import { staffMemberDetailsFragment } from "./queries"; +import { StaffAvatarDelete } from "./types/StaffAvatarDelete"; +import { + StaffAvatarUpdate, + StaffAvatarUpdateVariables +} from "./types/StaffAvatarUpdate"; import { StaffMemberAdd, StaffMemberAddVariables @@ -67,3 +72,45 @@ export const TypedStaffMemberDeleteMutation = TypedMutation< StaffMemberDelete, StaffMemberDeleteVariables >(staffMemberDeleteMutation); + +const staffAvatarUpdateMutation = gql` + mutation StaffAvatarUpdate($image: Upload!) { + userAvatarUpdate(image: $image) { + errors { + field + message + } + user { + id + avatar { + url + } + } + } + } +`; +export const TypedStaffAvatarUpdateMutation = TypedMutation< + StaffAvatarUpdate, + StaffAvatarUpdateVariables +>(staffAvatarUpdateMutation); + +const staffAvatarDeleteMutation = gql` + mutation StaffAvatarDelete { + userAvatarDelete { + errors { + field + message + } + user { + id + avatar { + url + } + } + } + } +`; +export const TypedStaffAvatarDeleteMutation = TypedMutation< + StaffAvatarDelete, + StaffMemberDeleteVariables +>(staffAvatarDeleteMutation); diff --git a/saleor/static/dashboard-next/staff/types/StaffAvatarDelete.ts b/saleor/static/dashboard-next/staff/types/StaffAvatarDelete.ts new file mode 100644 index 00000000000..763ee71e63d --- /dev/null +++ b/saleor/static/dashboard-next/staff/types/StaffAvatarDelete.ts @@ -0,0 +1,34 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL mutation operation: StaffAvatarDelete +// ==================================================== + +export interface StaffAvatarDelete_userAvatarDelete_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface StaffAvatarDelete_userAvatarDelete_user_avatar { + __typename: "Image"; + url: string; +} + +export interface StaffAvatarDelete_userAvatarDelete_user { + __typename: "User"; + id: string; + avatar: StaffAvatarDelete_userAvatarDelete_user_avatar | null; +} + +export interface StaffAvatarDelete_userAvatarDelete { + __typename: "UserAvatarDelete"; + errors: StaffAvatarDelete_userAvatarDelete_errors[] | null; + user: StaffAvatarDelete_userAvatarDelete_user | null; +} + +export interface StaffAvatarDelete { + userAvatarDelete: StaffAvatarDelete_userAvatarDelete | null; +} diff --git a/saleor/static/dashboard-next/staff/types/StaffAvatarUpdate.ts b/saleor/static/dashboard-next/staff/types/StaffAvatarUpdate.ts new file mode 100644 index 00000000000..847f5c80d09 --- /dev/null +++ b/saleor/static/dashboard-next/staff/types/StaffAvatarUpdate.ts @@ -0,0 +1,38 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL mutation operation: StaffAvatarUpdate +// ==================================================== + +export interface StaffAvatarUpdate_userAvatarUpdate_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface StaffAvatarUpdate_userAvatarUpdate_user_avatar { + __typename: "Image"; + url: string; +} + +export interface StaffAvatarUpdate_userAvatarUpdate_user { + __typename: "User"; + id: string; + avatar: StaffAvatarUpdate_userAvatarUpdate_user_avatar | null; +} + +export interface StaffAvatarUpdate_userAvatarUpdate { + __typename: "UserAvatarUpdate"; + errors: StaffAvatarUpdate_userAvatarUpdate_errors[] | null; + user: StaffAvatarUpdate_userAvatarUpdate_user | null; +} + +export interface StaffAvatarUpdate { + userAvatarUpdate: StaffAvatarUpdate_userAvatarUpdate | null; +} + +export interface StaffAvatarUpdateVariables { + image: any; +} diff --git a/saleor/static/dashboard-next/staff/urls.ts b/saleor/static/dashboard-next/staff/urls.ts index 2bf8ad3c7d0..56c2211a69b 100644 --- a/saleor/static/dashboard-next/staff/urls.ts +++ b/saleor/static/dashboard-next/staff/urls.ts @@ -14,10 +14,11 @@ export const staffListUrl = (params?: StaffListUrlQueryParams) => staffListPath + "?" + stringifyQs(params); export const staffMemberDetailsPath = (id: string) => urlJoin(staffSection, id); -export type StaffMemberDetailsUrlDialog = "remove"; +export type StaffMemberDetailsUrlDialog = "remove" | "remove-avatar"; export type StaffMemberDetailsUrlQueryParams = Dialog< StaffMemberDetailsUrlDialog >; + export const staffMemberDetailsUrl = ( id: string, params?: StaffMemberDetailsUrlQueryParams diff --git a/saleor/static/dashboard-next/staff/views/StaffDetails.tsx b/saleor/static/dashboard-next/staff/views/StaffDetails.tsx index 66e39a8ed68..f86ce92a810 100644 --- a/saleor/static/dashboard-next/staff/views/StaffDetails.tsx +++ b/saleor/static/dashboard-next/staff/views/StaffDetails.tsx @@ -10,10 +10,14 @@ import i18n from "../../i18n"; import { getMutationState, maybe } from "../../misc"; import StaffDetailsPage from "../components/StaffDetailsPage/StaffDetailsPage"; import { + TypedStaffAvatarDeleteMutation, + TypedStaffAvatarUpdateMutation, TypedStaffMemberDeleteMutation, TypedStaffMemberUpdateMutation } from "../mutations"; import { TypedStaffMemberDetailsQuery } from "../queries"; +import { StaffAvatarDelete } from "../types/StaffAvatarDelete"; +import { StaffAvatarUpdate } from "../types/StaffAvatarUpdate"; import { StaffMemberDelete } from "../types/StaffMemberDelete"; import { StaffMemberUpdate } from "../types/StaffMemberUpdate"; import { @@ -57,6 +61,21 @@ export const StaffDetails: React.StatelessComponent = ({ navigate(staffListUrl()); } }; + const handleStaffMemberAvatarUpdate = (data: StaffAvatarUpdate) => { + if (!maybe(() => data.userAvatarUpdate.errors.length !== 0)) { + notify({ + text: i18n.t("Succesfully updated staff member avatar") + }); + } + }; + const handleStaffMemberAvatarDelete = (data: StaffAvatarDelete) => { + if (!maybe(() => data.userAvatarDelete.errors.length !== 0)) { + notify({ + text: i18n.t("Succesfully removed staff member avatar") + }); + navigate(staffMemberDetailsUrl(id)); + } + }; return ( {(updateStaffMember, updateResult) => ( @@ -64,77 +83,137 @@ export const StaffDetails: React.StatelessComponent = ({ variables={{ id }} onCompleted={handleStaffMemberDelete} > - {(deleteStaffMember, deleteResult) => { - const formTransitionState = getMutationState( - updateResult.called, - updateResult.loading, - maybe(() => updateResult.data.staffUpdate.errors) - ); - const deleteTransitionState = getMutationState( - deleteResult.called, - deleteResult.loading, - maybe(() => deleteResult.data.staffDelete.errors) - ); - const isUserSameAsViewer = maybe( - () => user.user.id === data.user.id, - true - ); - - return ( - <> - data.user.email)} /> - navigate(staffListUrl())} - onDelete={() => - navigate( - staffMemberDetailsUrl(id, { - action: "remove" - }) - ) - } - onSubmit={variables => - updateStaffMember({ - variables: { - id, - input: { - email: variables.email, - firstName: variables.firstName, - isActive: variables.isActive, - lastName: variables.lastName, - permissions: variables.permissions - } - } - }) - } - permissions={maybe(() => data.shop.permissions)} - staffMember={maybe(() => data.user)} - saveButtonBarState={formTransitionState} - /> - navigate(staffMemberDetailsUrl(id))} - onConfirm={deleteStaffMember} + {(deleteStaffMember, deleteResult) => ( + + {updateStaffAvatar => ( + - {{ email }} from staff members?", - { - email: maybe(() => data.user.email) - } + {(deleteStaffAvatar, deleteAvatarResult) => { + const formTransitionState = getMutationState( + updateResult.called, + updateResult.loading, + maybe(() => updateResult.data.staffUpdate.errors) + ); + const deleteTransitionState = getMutationState( + deleteResult.called, + deleteResult.loading, + maybe(() => deleteResult.data.staffDelete.errors) + ); + const deleteAvatarTransitionState = getMutationState( + deleteAvatarResult.called, + deleteAvatarResult.loading, + maybe( + () => + deleteAvatarResult.data.userAvatarDelete.errors ) - }} - /> - - - ); - }} + ); + const isUserSameAsViewer = maybe( + () => user.user.id === data.user.id, + true + ); + + return ( + <> + data.user.email)} + /> + navigate(staffListUrl())} + onDelete={() => + navigate( + staffMemberDetailsUrl(id, { + action: "remove" + }) + ) + } + onSubmit={variables => + updateStaffMember({ + variables: { + id, + input: { + email: variables.email, + firstName: variables.firstName, + isActive: variables.isActive, + lastName: variables.lastName, + permissions: variables.permissions + } + } + }) + } + onImageUpload={file => + updateStaffAvatar({ + variables: { + image: file + } + }) + } + onImageDelete={() => + navigate( + staffMemberDetailsUrl(id, { + action: "remove-avatar" + }) + ) + } + permissions={maybe(() => data.shop.permissions)} + staffMember={maybe(() => data.user)} + saveButtonBarState={formTransitionState} + /> + + navigate(staffMemberDetailsUrl(id)) + } + onConfirm={deleteStaffMember} + > + {{ email }} from staff members?", + { + email: maybe(() => data.user.email) + } + ) + }} + /> + + + navigate(staffMemberDetailsUrl(id)) + } + onConfirm={deleteStaffAvatar} + > + {{ email }} avatar?", + { + email: maybe(() => data.user.email) + } + ) + }} + /> + + + ); + }} + + )} + + )} )} diff --git a/saleor/static/dashboard-next/storybook/__snapshots__/Stories.test.ts.snap b/saleor/static/dashboard-next/storybook/__snapshots__/Stories.test.ts.snap index db667aa492d..58620d51b7c 100644 --- a/saleor/static/dashboard-next/storybook/__snapshots__/Stories.test.ts.snap +++ b/saleor/static/dashboard-next/storybook/__snapshots__/Stories.test.ts.snap @@ -77853,6 +77853,28 @@ exports[`Storyshots Views / Staff / Staff member details himself 1`] = ` class="StaffProperties-avatarImage-id" src="avatar1.png" /> +
+ +

+ Change photo +

+

+ Delete photo +

+ +
@@ -78006,9 +78028,13 @@ exports[`Storyshots Views / Staff / Staff member details loading 1`] = `
- +
+

+

@@ -79987,9 +80013,13 @@ exports[`Storyshots Views / Staff / Staff members when loading 1`] = `
- +
+

+

= { + canEditAvatar: false, canEditStatus: true, canRemove: true, disabled: false, onBack: () => undefined, onDelete: () => undefined, + onImageDelete: () => undefined, + onImageUpload: () => undefined, onSubmit: () => undefined, permissions, saveButtonBarState: "default", @@ -36,5 +39,10 @@ storiesOf("Views / Staff / Staff member details", module) /> )) .add("himself", () => ( - + )); diff --git a/saleor/static/dashboard-next/types/globalTypes.ts b/saleor/static/dashboard-next/types/globalTypes.ts index 1a3ca41d9a7..7cc2d0d9b88 100644 --- a/saleor/static/dashboard-next/types/globalTypes.ts +++ b/saleor/static/dashboard-next/types/globalTypes.ts @@ -115,8 +115,12 @@ export enum OrderStatus { } export enum OrderStatusFilter { + CANCELED = "CANCELED", + FULFILLED = "FULFILLED", + PARTIALLY_FULFILLED = "PARTIALLY_FULFILLED", READY_TO_CAPTURE = "READY_TO_CAPTURE", READY_TO_FULFILL = "READY_TO_FULFILL", + UNFULFILLED = "UNFULFILLED", } export enum PaymentChargeStatusEnum { diff --git a/saleor/static/images/photo-icon.svg b/saleor/static/images/photo-icon.svg new file mode 100644 index 00000000000..57ee59835e8 --- /dev/null +++ b/saleor/static/images/photo-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + +