diff --git a/src/staff/components/StaffList/StaffList.tsx b/src/staff/components/StaffList/StaffList.tsx index eeefb9f97..f359027c6 100644 --- a/src/staff/components/StaffList/StaffList.tsx +++ b/src/staff/components/StaffList/StaffList.tsx @@ -1,4 +1,3 @@ -import Card from "@material-ui/core/Card"; import { createStyles, Theme, @@ -84,108 +83,102 @@ const StaffList = withStyles(styles, { name: "StaffList" })( const intl = useIntl(); return ( - - - - - - - - - - - - - - - + + + + - - - - {renderCollection( - staffMembers, - staffMember => ( - - -
- {maybe(() => staffMember.avatar.url) ? ( - staffMember.avatar.url)} - /> - ) : ( -
- - {getUserInitials(staffMember)} - -
- )} -
- - {getUserName(staffMember) || } - - - {maybe( - () => - staffMember.isActive - ? intl.formatMessage({ - defaultMessage: "Active", - description: "staff member status" - }) - : intl.formatMessage({ - defaultMessage: "Inactive", - description: "staff member status" - }), - - )} - -
- + + + + +
+ + + + + + + + {renderCollection( + staffMembers, + staffMember => ( + + +
+ {maybe(() => staffMember.avatar.url) ? ( + staffMember.avatar.url)} + /> + ) : ( +
+ {getUserInitials(staffMember)} +
+ )} +
+ + {getUserName(staffMember) || } + + {maybe( - () => staffMember.email, + () => + staffMember.isActive + ? intl.formatMessage({ + defaultMessage: "Active", + description: "staff member status" + }) + : intl.formatMessage({ + defaultMessage: "Inactive", + description: "staff member status" + }), )} -
-
- ), - () => ( - - - - - - ) - )} -
-
-
+ + + + {maybe( + () => staffMember.email, + + )} + + + ), + () => ( + + + + + + ) + )} + + ); } ); diff --git a/src/staff/components/StaffListPage/StaffListPage.tsx b/src/staff/components/StaffListPage/StaffListPage.tsx index 5f4faa668..f83335e56 100644 --- a/src/staff/components/StaffListPage/StaffListPage.tsx +++ b/src/staff/components/StaffListPage/StaffListPage.tsx @@ -1,26 +1,37 @@ import Button from "@material-ui/core/Button"; - +import Card from "@material-ui/core/Card"; import React from "react"; import { FormattedMessage, useIntl } from "react-intl"; import AppHeader from "@saleor/components/AppHeader"; import { Container } from "@saleor/components/Container"; import PageHeader from "@saleor/components/PageHeader"; +import SearchBar from "@saleor/components/SearchBar"; import { sectionNames } from "@saleor/intl"; -import { ListProps } from "@saleor/types"; +import { ListProps, SearchPageProps, TabPageProps } from "@saleor/types"; import { StaffList_staffUsers_edges_node } from "../../types/StaffList"; import StaffList from "../StaffList/StaffList"; -export interface StaffListPageProps extends ListProps { +export interface StaffListPageProps + extends ListProps, + SearchPageProps, + TabPageProps { staffMembers: StaffList_staffUsers_edges_node[]; onAdd: () => void; onBack: () => void; } const StaffListPage: React.StatelessComponent = ({ - disabled, + currentTab, + initialSearch, onAdd, + onAll, onBack, + onSearchChange, + onTabChange, + onTabDelete, + onTabSave, + tabs, ...listProps }) => { const intl = useIntl(); @@ -31,19 +42,33 @@ const StaffListPage: React.StatelessComponent = ({ {intl.formatMessage(sectionNames.configuration)} - - + + + + ); }; diff --git a/src/staff/queries.ts b/src/staff/queries.ts index 994b2f47c..5bb538eb4 100644 --- a/src/staff/queries.ts +++ b/src/staff/queries.ts @@ -30,8 +30,20 @@ export const staffMemberDetailsFragment = gql` `; const staffList = gql` ${staffMemberFragment} - query StaffList($first: Int, $after: String, $last: Int, $before: String) { - staffUsers(before: $before, after: $after, first: $first, last: $last) { + query StaffList( + $first: Int + $after: String + $last: Int + $before: String + $filter: StaffUserInput + ) { + staffUsers( + before: $before + after: $after + first: $first + last: $last + filter: $filter + ) { edges { cursor node { diff --git a/src/staff/types/StaffList.ts b/src/staff/types/StaffList.ts index 2a3663628..08f0a6471 100644 --- a/src/staff/types/StaffList.ts +++ b/src/staff/types/StaffList.ts @@ -2,7 +2,7 @@ /* eslint-disable */ // This file was automatically generated and should not be edited. -import { PermissionEnum } from "./../../types/globalTypes"; +import { StaffUserInput, PermissionEnum } from "./../../types/globalTypes"; // ==================================================== // GraphQL query operation: StaffList @@ -64,4 +64,5 @@ export interface StaffListVariables { after?: string | null; last?: number | null; before?: string | null; + filter?: StaffUserInput | null; } diff --git a/src/staff/urls.ts b/src/staff/urls.ts index 4508afdfd..7bdf6e201 100644 --- a/src/staff/urls.ts +++ b/src/staff/urls.ts @@ -1,15 +1,28 @@ import { stringify as stringifyQs } from "qs"; import urlJoin from "url-join"; -import { BulkAction, Dialog, Pagination } from "../types"; +import { + ActiveTab, + BulkAction, + Dialog, + Filters, + Pagination, + TabActionDialog +} from "../types"; const staffSection = "/staff/"; export const staffListPath = staffSection; -export type StaffListUrlDialog = "add" | "remove"; -export type StaffListUrlQueryParams = BulkAction & +export enum StaffListUrlFiltersEnum { + query = "query" +} +export type StaffListUrlFilters = Filters; +export type StaffListUrlDialog = "add" | "remove" | TabActionDialog; +export type StaffListUrlQueryParams = ActiveTab & + BulkAction & Dialog & - Pagination; + Pagination & + StaffListUrlFilters; export const staffListUrl = (params?: StaffListUrlQueryParams) => staffListPath + "?" + stringifyQs(params); diff --git a/src/staff/views/StaffList.tsx b/src/staff/views/StaffList/StaffList.tsx similarity index 59% rename from src/staff/views/StaffList.tsx rename to src/staff/views/StaffList/StaffList.tsx index 0cc4ad33e..eb104ead5 100644 --- a/src/staff/views/StaffList.tsx +++ b/src/staff/views/StaffList/StaffList.tsx @@ -8,22 +8,36 @@ import usePaginator, { } from "@saleor/hooks/usePaginator"; import { useIntl } from "react-intl"; +import DeleteFilterTabDialog from "@saleor/components/DeleteFilterTabDialog"; +import SaveFilterTabDialog, { + SaveFilterTabDialogFormData +} from "@saleor/components/SaveFilterTabDialog"; import { configurationMenuUrl } from "@saleor/configuration"; import { commonMessages } from "@saleor/intl"; import { getMutationState, maybe } from "@saleor/misc"; import { ListViews } from "@saleor/types"; import StaffAddMemberDialog, { FormData as AddStaffMemberForm -} from "../components/StaffAddMemberDialog"; -import StaffListPage from "../components/StaffListPage"; -import { TypedStaffMemberAddMutation } from "../mutations"; -import { TypedStaffListQuery } from "../queries"; -import { StaffMemberAdd } from "../types/StaffMemberAdd"; +} from "../../components/StaffAddMemberDialog"; +import StaffListPage from "../../components/StaffListPage"; +import { TypedStaffMemberAddMutation } from "../../mutations"; +import { TypedStaffListQuery } from "../../queries"; +import { StaffMemberAdd } from "../../types/StaffMemberAdd"; import { staffListUrl, + StaffListUrlDialog, + StaffListUrlFilters, StaffListUrlQueryParams, staffMemberDetailsUrl -} from "../urls"; +} from "../../urls"; +import { + areFiltersApplied, + deleteFilterTab, + getActiveFilters, + getFilterTabs, + getFilterVariables, + saveFilterTab +} from "./filter"; interface StaffListProps { params: StaffListUrlQueryParams; @@ -40,19 +54,72 @@ export const StaffList: React.StatelessComponent = ({ ); const intl = useIntl(); + const tabs = getFilterTabs(); + + const currentTab = + params.activeTab === undefined + ? areFiltersApplied(params) + ? tabs.length + 1 + : 0 + : parseInt(params.activeTab, 0); + + const changeFilterField = (filter: StaffListUrlFilters) => + navigate( + staffListUrl({ + ...getActiveFilters(params), + ...filter, + activeTab: undefined + }) + ); + const closeModal = () => navigate( staffListUrl({ ...params, action: undefined, ids: undefined - }), - true + }) ); + const openModal = (action: StaffListUrlDialog, ids?: string[]) => + navigate( + staffListUrl({ + ...params, + action, + ids + }) + ); + + const handleTabChange = (tab: number) => { + navigate( + staffListUrl({ + activeTab: tab.toString(), + ...getFilterTabs()[tab - 1].data + }) + ); + }; + + const handleTabDelete = () => { + deleteFilterTab(currentTab); + navigate(staffListUrl()); + }; + + const handleTabSave = (data: SaveFilterTabDialogFormData) => { + saveFilterTab(data.name, getActiveFilters(params)); + handleTabChange(tabs.length + 1); + }; + const paginationState = createPaginationState(settings.rowNumber, params); + const queryVariables = React.useMemo( + () => ({ + ...paginationState, + filter: getFilterVariables(params) + }), + [params] + ); + return ( - + {({ data, loading }) => { const handleStaffMemberAddSuccess = (data: StaffMemberAdd) => { if (data.staffCreate.errors.length === 0) { @@ -97,6 +164,14 @@ export const StaffList: React.StatelessComponent = ({ return ( <> changeFilterField({ query })} + onAll={() => navigate(staffListUrl())} + onTabChange={handleTabChange} + onTabDelete={() => openModal("delete-search")} + onTabSave={() => openModal("save-search")} + tabs={tabs.map(tab => tab.name)} disabled={loading || addStaffMemberData.loading} settings={settings} pageInfo={pageInfo} @@ -126,6 +201,19 @@ export const StaffList: React.StatelessComponent = ({ onClose={closeModal} onConfirm={handleStaffMemberAdd} /> + + tabs[currentTab - 1].name, "...")} + /> ); }} diff --git a/src/staff/views/StaffList/filter.ts b/src/staff/views/StaffList/filter.ts new file mode 100644 index 000000000..9223a0372 --- /dev/null +++ b/src/staff/views/StaffList/filter.ts @@ -0,0 +1,31 @@ +import { StaffUserInput } from "@saleor/types/globalTypes"; +import { + createFilterTabUtils, + createFilterUtils +} from "../../../utils/filters"; +import { + StaffListUrlFilters, + StaffListUrlFiltersEnum, + StaffListUrlQueryParams +} from "../../urls"; + +export const STAFF_FILTERS_KEY = "staffFilters"; + +export function getFilterVariables( + params: StaffListUrlFilters +): StaffUserInput { + return { + search: params.query + }; +} + +export const { + deleteFilterTab, + getFilterTabs, + saveFilterTab +} = createFilterTabUtils(STAFF_FILTERS_KEY); + +export const { areFiltersApplied, getActiveFilters } = createFilterUtils< + StaffListUrlQueryParams, + StaffListUrlFilters +>(StaffListUrlFiltersEnum); diff --git a/src/staff/views/StaffList/index.ts b/src/staff/views/StaffList/index.ts new file mode 100644 index 000000000..524579391 --- /dev/null +++ b/src/staff/views/StaffList/index.ts @@ -0,0 +1,2 @@ +export { default } from "./StaffList"; +export * from "./StaffList"; diff --git a/src/types/globalTypes.ts b/src/types/globalTypes.ts index 3cb2b2084..1854f0ea0 100644 --- a/src/types/globalTypes.ts +++ b/src/types/globalTypes.ts @@ -197,6 +197,11 @@ export enum ShippingMethodTypeEnum { WEIGHT = "WEIGHT", } +export enum StaffMemberStatus { + ACTIVE = "ACTIVE", + DEACTIVATED = "DEACTIVATED", +} + export enum StockAvailability { IN_STOCK = "IN_STOCK", OUT_OF_STOCK = "OUT_OF_STOCK", @@ -659,6 +664,11 @@ export interface StaffInput { permissions?: (PermissionEnum | null)[] | null; } +export interface StaffUserInput { + status?: StaffMemberStatus | null; + search?: string | null; +} + export interface TranslationInput { seoTitle?: string | null; seoDescription?: string | null;