diff --git a/src/services/components/ServiceDeleteDialog/ServiceDeleteDialog.stories.tsx b/src/services/components/ServiceDeleteDialog/ServiceDeleteDialog.stories.tsx new file mode 100644 index 000000000..c43118c72 --- /dev/null +++ b/src/services/components/ServiceDeleteDialog/ServiceDeleteDialog.stories.tsx @@ -0,0 +1,19 @@ +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import Decorator from "@saleor/storybook/Decorator"; +import ServiceDeleteDialog, { + ServiceDeleteDialogProps +} from "./ServiceDeleteDialog"; + +const props: ServiceDeleteDialogProps = { + confirmButtonState: "default", + name: "Magento Importer", + onClose: () => undefined, + onConfirm: () => undefined, + open: true +}; + +storiesOf("Views / Services / Delete service", module) + .addDecorator(Decorator) + .add("default", () => ); diff --git a/src/services/components/ServiceDeleteDialog/ServiceDeleteDialog.tsx b/src/services/components/ServiceDeleteDialog/ServiceDeleteDialog.tsx new file mode 100644 index 000000000..ec83a9494 --- /dev/null +++ b/src/services/components/ServiceDeleteDialog/ServiceDeleteDialog.tsx @@ -0,0 +1,50 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import React from "react"; +import { FormattedMessage, useIntl } from "react-intl"; + +import ActionDialog from "@saleor/components/ActionDialog"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; + +export interface ServiceDeleteDialogProps { + confirmButtonState: ConfirmButtonTransitionState; + open: boolean; + name: string; + onClose: () => void; + onConfirm: () => void; +} + +const ServiceDeleteDialog: React.FC = ({ + confirmButtonState, + open, + name, + onClose, + onConfirm +}) => { + const intl = useIntl(); + + return ( + + + {name} + }} + /> + + + ); +}; +ServiceDeleteDialog.displayName = "ServiceDeleteDialog"; +export default ServiceDeleteDialog; diff --git a/src/services/components/ServiceDeleteDialog/index.ts b/src/services/components/ServiceDeleteDialog/index.ts new file mode 100644 index 000000000..b43358431 --- /dev/null +++ b/src/services/components/ServiceDeleteDialog/index.ts @@ -0,0 +1,2 @@ +export { default } from "./ServiceDeleteDialog"; +export * from "./ServiceDeleteDialog"; diff --git a/src/services/components/ServiceList/ServiceList.tsx b/src/services/components/ServiceList/ServiceList.tsx index 78b730396..9efeb2899 100644 --- a/src/services/components/ServiceList/ServiceList.tsx +++ b/src/services/components/ServiceList/ServiceList.tsx @@ -17,10 +17,9 @@ import EditIcon from "@material-ui/icons/Edit"; import React from "react"; import { FormattedMessage } from "react-intl"; -import Checkbox from "@saleor/components/Checkbox"; import Skeleton from "@saleor/components/Skeleton"; import TablePagination from "@saleor/components/TablePagination"; -import { maybe, renderCollection } from "@saleor/misc"; +import { maybe, renderCollection, stopPropagation } from "@saleor/misc"; import { ListProps } from "@saleor/types"; import { ServiceList_serviceAccounts_edges_node } from "../../types/ServiceList"; @@ -76,10 +75,15 @@ const ServiceList = withStyles(styles, { }: ServiceListProps & WithStyles) => ( - - - - + + + + + + @@ -135,7 +139,11 @@ const ServiceList = withStyles(styles, { onRemove(service.id) : undefined} + onClick={ + service + ? stopPropagation(() => onRemove(service.id)) + : undefined + } > diff --git a/src/services/mutations.ts b/src/services/mutations.ts index cbec091e5..b28f0cdb3 100644 --- a/src/services/mutations.ts +++ b/src/services/mutations.ts @@ -1,10 +1,12 @@ import gql from "graphql-tag"; import { TypedMutation } from "../mutations"; -import { serviceFragment } from "./queries"; +import { serviceDetailsFragment, serviceFragment } from "./queries"; import { ServiceCreate, ServiceCreateVariables } from "./types/ServiceCreate"; +import { ServiceDelete, ServiceDeleteVariables } from "./types/ServiceDelete"; +import { ServiceUpdate, ServiceUpdateVariables } from "./types/ServiceUpdate"; -export const serviceCreateMutation = gql` +const serviceCreateMutation = gql` ${serviceFragment} mutation ServiceCreate($input: ServiceAccountInput!) { serviceAccountCreate(input: $input) { @@ -18,7 +20,43 @@ export const serviceCreateMutation = gql` } } `; + export const ServiceCreateMutation = TypedMutation< ServiceCreate, ServiceCreateVariables >(serviceCreateMutation); + +const serviceDeleteMutation = gql` + mutation ServiceDelete($id: ID!) { + serviceAccountDelete(id: $id) { + errors { + field + message + } + } + } +`; +export const ServiceDeleteMutation = TypedMutation< + ServiceDelete, + ServiceDeleteVariables +>(serviceDeleteMutation); + +const serviceUpdateMutation = gql` + ${serviceDetailsFragment} + mutation ServiceUpdate($id: ID!, $input: ServiceAccountInput!) { + serviceAccountUpdate(id: $id, input: $input) { + errors { + field + message + } + serviceAccount { + ...ServiceDetailsFragment + } + } + } +`; + +export const ServiceUpdateMutation = TypedMutation< + ServiceUpdate, + ServiceUpdateVariables +>(serviceUpdateMutation); diff --git a/src/services/queries.ts b/src/services/queries.ts index f7327fc50..7263670d3 100644 --- a/src/services/queries.ts +++ b/src/services/queries.ts @@ -47,7 +47,7 @@ export const ServiceListQuery = TypedQuery( serviceList ); -const serviceDetailsFragment = gql` +export const serviceDetailsFragment = gql` ${serviceFragment} fragment ServiceDetailsFragment on ServiceAccount { ...ServiceFragment diff --git a/src/services/types/ServiceDelete.ts b/src/services/types/ServiceDelete.ts new file mode 100644 index 000000000..c339507d8 --- /dev/null +++ b/src/services/types/ServiceDelete.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL mutation operation: ServiceDelete +// ==================================================== + +export interface ServiceDelete_serviceAccountDelete_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface ServiceDelete_serviceAccountDelete { + __typename: "ServiceAccountDelete"; + errors: ServiceDelete_serviceAccountDelete_errors[] | null; +} + +export interface ServiceDelete { + serviceAccountDelete: ServiceDelete_serviceAccountDelete | null; +} + +export interface ServiceDeleteVariables { + id: string; +} diff --git a/src/services/types/ServiceUpdate.ts b/src/services/types/ServiceUpdate.ts new file mode 100644 index 000000000..f267ca6c9 --- /dev/null +++ b/src/services/types/ServiceUpdate.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +// This file was automatically generated and should not be edited. + +import { ServiceAccountInput, PermissionEnum } from "./../../types/globalTypes"; + +// ==================================================== +// GraphQL mutation operation: ServiceUpdate +// ==================================================== + +export interface ServiceUpdate_serviceAccountUpdate_errors { + __typename: "Error"; + field: string | null; + message: string | null; +} + +export interface ServiceUpdate_serviceAccountUpdate_serviceAccount_permissions { + __typename: "PermissionDisplay"; + code: PermissionEnum; + name: string; +} + +export interface ServiceUpdate_serviceAccountUpdate_serviceAccount_tokens { + __typename: "ServiceAccountToken"; + id: string; + name: string | null; + authToken: string | null; +} + +export interface ServiceUpdate_serviceAccountUpdate_serviceAccount { + __typename: "ServiceAccount"; + id: string; + name: string | null; + isActive: boolean | null; + permissions: (ServiceUpdate_serviceAccountUpdate_serviceAccount_permissions | null)[] | null; + tokens: (ServiceUpdate_serviceAccountUpdate_serviceAccount_tokens | null)[] | null; +} + +export interface ServiceUpdate_serviceAccountUpdate { + __typename: "ServiceAccountUpdate"; + errors: ServiceUpdate_serviceAccountUpdate_errors[] | null; + serviceAccount: ServiceUpdate_serviceAccountUpdate_serviceAccount | null; +} + +export interface ServiceUpdate { + serviceAccountUpdate: ServiceUpdate_serviceAccountUpdate | null; +} + +export interface ServiceUpdateVariables { + id: string; + input: ServiceAccountInput; +} diff --git a/src/services/urls.ts b/src/services/urls.ts index 0c78289ee..3fb758a6d 100644 --- a/src/services/urls.ts +++ b/src/services/urls.ts @@ -3,7 +3,6 @@ import urlJoin from "url-join"; import { ActiveTab, - BulkAction, Dialog, Filters, Pagination, @@ -20,10 +19,10 @@ export enum ServiceListUrlFiltersEnum { export type ServiceListUrlFilters = Filters; export type ServiceListUrlDialog = "remove" | TabActionDialog; export type ServiceListUrlQueryParams = ActiveTab & - BulkAction & ServiceListUrlFilters & Dialog & - Pagination; + Pagination & + SingleAction; export const serviceListUrl = (params?: ServiceListUrlQueryParams) => serviceListPath + "?" + stringifyQs(params); diff --git a/src/services/views/ServiceList/ServiceList.tsx b/src/services/views/ServiceList/ServiceList.tsx index 9cd843fe6..6e102fd16 100644 --- a/src/services/views/ServiceList/ServiceList.tsx +++ b/src/services/views/ServiceList/ServiceList.tsx @@ -16,7 +16,10 @@ import { configurationMenuUrl } from "@saleor/configuration"; import useShop from "@saleor/hooks/useShop"; import { commonMessages } from "@saleor/intl"; import { getMutationState, maybe } from "@saleor/misc"; +import { ServiceDeleteMutation } from "@saleor/services/mutations"; +import { ServiceDelete } from "@saleor/services/types/ServiceDelete"; import { ListViews } from "@saleor/types"; +import ServiceDeleteDialog from "../../components/ServiceDeleteDialog"; import ServiceListPage from "../../components/ServiceListPage"; import { ServiceListQuery } from "../../queries"; import { @@ -50,7 +53,6 @@ export const ServiceList: React.StatelessComponent = ({ ListViews.STAFF_MEMBERS_LIST ); const intl = useIntl(); - const shop = useShop(); const tabs = getFilterTabs(); @@ -75,17 +77,17 @@ export const ServiceList: React.StatelessComponent = ({ serviceListUrl({ ...params, action: undefined, - ids: undefined + id: undefined }), true ); - const openModal = (action: ServiceListUrlDialog, ids?: string[]) => + const openModal = (action: ServiceListUrlDialog, id?: string) => navigate( serviceListUrl({ ...params, action, - ids + id }) ); @@ -119,7 +121,7 @@ export const ServiceList: React.StatelessComponent = ({ return ( - {({ data, loading }) => { + {({ data, loading, refetch }) => { const { loadNextPage, loadPreviousPage, pageInfo } = paginate( maybe(() => data.serviceAccounts.pageInfo), paginationState, @@ -127,40 +129,95 @@ export const ServiceList: React.StatelessComponent = ({ ); const handleCreate = () => navigate(serviceAddUrl); - const handleRemove = () => + const handleRemove = (id: string) => navigate( serviceListUrl({ ...params, - action: "remove" + action: "remove", + id }) ); + const onRemove = (data: ServiceDelete) => { + if (data.serviceAccountDelete.errors.length === 0) { + notify({ + text: intl.formatMessage(commonMessages.savedChanges) + }); + closeModal(); + refetch(); + } + }; return ( - <> - changeFilterField({ query })} - onAll={() => navigate(serviceListUrl())} - onTabChange={handleTabChange} - onTabDelete={() => openModal("delete-search")} - onTabSave={() => openModal("save-search")} - tabs={tabs.map(tab => tab.name)} - disabled={loading} - settings={settings} - pageInfo={pageInfo} - services={maybe(() => - data.serviceAccounts.edges.map(edge => edge.node) - )} - onAdd={handleCreate} - onBack={() => navigate(configurationMenuUrl)} - onNextPage={loadNextPage} - onPreviousPage={loadPreviousPage} - onUpdateListSettings={updateListSettings} - onRowClick={id => () => navigate(serviceUrl(id))} - onRemove={handleRemove} - /> - + + {(deleteService, deleteServiceOpts) => { + const handleRemoveConfirm = () => + deleteService({ + variables: { + id: params.id + } + }); + + const removeTransitionState = getMutationState( + deleteServiceOpts.called, + deleteServiceOpts.loading, + maybe(() => deleteServiceOpts.data.serviceAccountDelete.errors) + ); + + return ( + <> + changeFilterField({ query })} + onAll={() => navigate(serviceListUrl())} + onTabChange={handleTabChange} + onTabDelete={() => openModal("delete-search")} + onTabSave={() => openModal("save-search")} + tabs={tabs.map(tab => tab.name)} + disabled={loading} + settings={settings} + pageInfo={pageInfo} + services={maybe(() => + data.serviceAccounts.edges.map(edge => edge.node) + )} + onAdd={handleCreate} + onBack={() => navigate(configurationMenuUrl)} + onNextPage={loadNextPage} + onPreviousPage={loadPreviousPage} + onUpdateListSettings={updateListSettings} + onRowClick={id => () => navigate(serviceUrl(id))} + onRemove={handleRemove} + /> + + data.serviceAccounts.edges.find( + edge => edge.node.id === params.id + ).node.name, + "..." + )} + onClose={closeModal} + onConfirm={handleRemoveConfirm} + open={params.action === "remove"} + /> + + tabs[currentTab - 1].name, "...")} + /> + + ); + }} + ); }}