Skip to content

Commit

Permalink
Home onboarding logic with env setup (#5274)
Browse files Browse the repository at this point in the history
* feature: Dashboard UI onboarding component

* i18n

* feature: Dashboard UI onboarding component

* i18n

* Fetch dateJoined from user query

* Add handle check threshold date for new users

* Refactor onboarding logic

* Add onboarding step complete triggers

* Add permissions checks to buttons

* Fix links in buttons

* Fix test and improve logic

* add test to step modifications and small refactor

* add toggle handling when step exists during toggling

* Add example env

* Improve toggle accordion logic and tests

* Withdraw auth changes

* Add changeset

* Extract messages

* Simplify code

* Final fixes, refactors and tests

* Fix calulcate expanded id

* Add ONBOARDING_USER_JOINED_DATE_THRESHOLD to workflow

* Fix include

* Extract messages

* Use vars instead of secrets

* Remove env from pipelines

* Add fallback to env date

* Remove duplicated useOnboardingData.tsx

---------

Co-authored-by: Wojciech <[email protected]>
Co-authored-by: M.Graczyk <[email protected]>
  • Loading branch information
3 people authored Nov 26, 2024
1 parent 8c61451 commit 2af53a8
Show file tree
Hide file tree
Showing 29 changed files with 895 additions and 290 deletions.
5 changes: 5 additions & 0 deletions .changeset/fresh-ears-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

User onboardng steps are now checking when user does required actions
4 changes: 3 additions & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ MAILPITURL=xxxx #For playwright
E2E_USER_NAME=xxxx
E2E_USER_PASSWORD=xxxx
E2E_PERMISSIONS_USERS_PASSWORD=xxxx
BASE_URL=http://localhost:9000/
BASE_URL=http://localhost:9000/

ONBOARDING_USER_JOINED_DATE_THRESHOLD=2024-07-01
14 changes: 10 additions & 4 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3486,6 +3486,9 @@
"context": "tooltip",
"string": "This feature is in a preview state and can be subject to changes at later point"
},
"KHI/qv": {
"string": "You don't have permission to manage products"
},
"KHZlmi": {
"string": "Discount Type"
},
Expand Down Expand Up @@ -4510,10 +4513,6 @@
"context": "info text",
"string": "Set up an end date of preorder. When end date will be reached product will be automatically taken from preorder to standard selling"
},
"RFXT9O": {
"context": "tooltip message",
"string": "You don't have permission to invite staff members"
},
"RH+aOF": {
"context": "use attribute in filtering",
"string": "Use in Filtering"
Expand Down Expand Up @@ -6913,6 +6912,10 @@
"grkY2V": {
"string": "You don't have access to any channels"
},
"gt05TH": {
"context": "tooltip message",
"string": "You don't have permission to manage staff"
},
"gvOzOl": {
"string": "Page Title"
},
Expand Down Expand Up @@ -9425,6 +9428,9 @@
"context": "dialog title",
"string": "Delete category"
},
"xol6jX": {
"string": "You don't have permission to manage orders"
},
"xoyCZ/": {
"context": "error message",
"string": "Improper value"
Expand Down
17 changes: 15 additions & 2 deletions src/components/DevModePanel/DevModePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// @ts-strict-ignore
import { useDashboardTheme } from "@dashboard/components/GraphiQL/styles";
import { DashboardModal } from "@dashboard/components/Modal";
import { createGraphiQLFetcher } from "@graphiql/toolkit";
import { useFlag } from "@dashboard/featureFlags";
import { useOnboarding } from "@dashboard/newHome/homeOnboarding/onboardingContext";
import { createGraphiQLFetcher, FetcherOpts, FetcherParams } from "@graphiql/toolkit";
import { createFetch } from "@saleor/sdk";
import React from "react";
import { useIntl } from "react-intl";
Expand All @@ -15,11 +17,22 @@ const authorizedFetch = createFetch();
export const DevModePanel: React.FC = () => {
const intl = useIntl();
const { rootStyle } = useDashboardTheme();
const { markOnboardingStepAsCompleted } = useOnboarding();
const newHomePageFlag = useFlag("new_home_page");
const { isDevModeVisible, variables, devModeContent, setDevModeVisibility } = useDevModeContext();
const fetcher = createGraphiQLFetcher({
const baseFetcher = createGraphiQLFetcher({
url: process.env.API_URL,
fetch: authorizedFetch,
});
const fetcher = async (graphQLParams: FetcherParams, opts: FetcherOpts) => {
if (graphQLParams.operationName !== "IntrospectionQuery" && newHomePageFlag.enabled) {
markOnboardingStepAsCompleted("graphql-playground");
}

const result = await baseFetcher(graphQLParams, opts); // Call the base fetcher

return result;
};
const overwriteCodeMirrorCSSVariables = {
__html: `
.graphiql-container, .CodeMirror-info, .CodeMirror-lint-tooltip, reach-portal{
Expand Down
13 changes: 12 additions & 1 deletion src/custom-apps/views/CustomAppList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useApolloClient } from "@apollo/client";
import AppDeleteDialog from "@dashboard/apps/components/AppDeleteDialog";
import { EXTENSION_LIST_QUERY } from "@dashboard/apps/queries";
import { WindowTitle } from "@dashboard/components/WindowTitle";
import { useFlag } from "@dashboard/featureFlags";
import {
AppSortField,
AppTypeEnum,
Expand All @@ -14,9 +15,10 @@ import useNavigator from "@dashboard/hooks/useNavigator";
import useNotifier from "@dashboard/hooks/useNotifier";
import { sectionNames } from "@dashboard/intl";
import { findById } from "@dashboard/misc";
import { useOnboarding } from "@dashboard/newHome/homeOnboarding/onboardingContext";
import createDialogActionHandlers from "@dashboard/utils/handlers/dialogActionHandlers";
import { mapEdgesToItems } from "@dashboard/utils/maps";
import React from "react";
import React, { useEffect } from "react";
import { useIntl } from "react-intl";

import CustomAppListPage from "../components/CustomAppListPage";
Expand All @@ -32,6 +34,15 @@ export const CustomAppList: React.FC<CustomAppListProps> = ({ params }) => {
const notify = useNotifier();
const intl = useIntl();
const client = useApolloClient();
const { markOnboardingStepAsCompleted } = useOnboarding();
const newHomePageFlag = useFlag("new_home_page");

useEffect(() => {
if (newHomePageFlag) {
markOnboardingStepAsCompleted("view-webhooks");
}
}, []);

const [openModal, closeModal] = createDialogActionHandlers<
CustomAppListUrlDialog,
CustomAppListUrlQueryParams
Expand Down
1 change: 1 addition & 0 deletions src/fragments/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const fragmentUser = gql`
firstName
lastName
isStaff
dateJoined
metadata {
key
value
Expand Down
1 change: 1 addition & 0 deletions src/graphql/hooks.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ export const UserFragmentDoc = gql`
firstName
lastName
isStaff
dateJoined
metadata {
key
value
Expand Down
4 changes: 2 additions & 2 deletions src/graphql/types.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9172,7 +9172,7 @@ export type AvailableExternalAuthenticationsQuery = { __typename: 'Query', shop:
export type UserDetailsQueryVariables = Exact<{ [key: string]: never; }>;


export type UserDetailsQuery = { __typename: 'Query', me: { __typename: 'User', id: string, email: string, firstName: string, lastName: string, isStaff: boolean, restrictedAccessToChannels: boolean, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, userPermissions: Array<{ __typename: 'UserPermission', code: PermissionEnum, name: string }> | null, avatar: { __typename: 'Image', url: string } | null, accessibleChannels: Array<{ __typename: 'Channel', id: string, isActive: boolean, name: string, slug: string, currencyCode: string, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } }> | null } | null };
export type UserDetailsQuery = { __typename: 'Query', me: { __typename: 'User', id: string, email: string, firstName: string, lastName: string, isStaff: boolean, dateJoined: any, restrictedAccessToChannels: boolean, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, userPermissions: Array<{ __typename: 'UserPermission', code: PermissionEnum, name: string }> | null, avatar: { __typename: 'Image', url: string } | null, accessibleChannels: Array<{ __typename: 'Channel', id: string, isActive: boolean, name: string, slug: string, currencyCode: string, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } }> | null } | null };

export type CategoryDeleteMutationVariables = Exact<{
id: Scalars['ID'];
Expand Down Expand Up @@ -9996,7 +9996,7 @@ export type UserPermissionFragment = { __typename: 'UserPermission', code: Permi

export type UserUserPermissionWithSourcePermissionGroupsFragment = { __typename: 'UserPermission', code: PermissionEnum, name: string, sourcePermissionGroups: Array<{ __typename: 'Group', id: string }> | null };

export type UserFragment = { __typename: 'User', id: string, email: string, firstName: string, lastName: string, isStaff: boolean, restrictedAccessToChannels: boolean, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, userPermissions: Array<{ __typename: 'UserPermission', code: PermissionEnum, name: string }> | null, avatar: { __typename: 'Image', url: string } | null, accessibleChannels: Array<{ __typename: 'Channel', id: string, isActive: boolean, name: string, slug: string, currencyCode: string, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } }> | null };
export type UserFragment = { __typename: 'User', id: string, email: string, firstName: string, lastName: string, isStaff: boolean, dateJoined: any, restrictedAccessToChannels: boolean, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, userPermissions: Array<{ __typename: 'UserPermission', code: PermissionEnum, name: string }> | null, avatar: { __typename: 'Image', url: string } | null, accessibleChannels: Array<{ __typename: 'Channel', id: string, isActive: boolean, name: string, slug: string, currencyCode: string, defaultCountry: { __typename: 'CountryDisplay', code: string, country: string }, stockSettings: { __typename: 'StockSettings', allocationStrategy: AllocationStrategyEnum } }> | null };

export type UserBaseFragment = { __typename: 'User', id: string, firstName: string, lastName: string };

Expand Down
34 changes: 1 addition & 33 deletions src/newHome/homeOnboarding/HomeCheckGraphQLButton.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,10 @@
import { useDevModeContext } from "@dashboard/components/DevModePanel/hooks";
import { Button, Tooltip } from "@saleor/macaw-ui-next";
import { Button } from "@saleor/macaw-ui-next";
import React from "react";
import { FormattedMessage } from "react-intl";

import { HomeFakeDisabledButton } from "./HomeFakeDisabledButton";

export const HomeCheckGraphQLButton = () => {
const context = useDevModeContext();
const getTooltipContent = () => {
return {
reason: "",
message: "",
};
};

const canViewGraphQLPlayground = true;

if (!canViewGraphQLPlayground) {
const { message } = getTooltipContent();

return (
<Tooltip>
<Tooltip.Trigger>
<HomeFakeDisabledButton>
<FormattedMessage
defaultMessage="Go to GraphQL Playground"
id="2xkzcr"
description="btn label"
/>
</HomeFakeDisabledButton>
</Tooltip.Trigger>
<Tooltip.Content>
<Tooltip.Arrow />
{message}
</Tooltip.Content>
</Tooltip>
);
}

return (
<Button
Expand Down
42 changes: 19 additions & 23 deletions src/newHome/homeOnboarding/HomeCreateProductButton.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import { useUser } from "@dashboard/auth";
import { hasPermissions } from "@dashboard/components/RequirePermissions";
import { PermissionEnum } from "@dashboard/graphql";
import { productListUrl } from "@dashboard/products/urls";
import { Button, Tooltip } from "@saleor/macaw-ui-next";
import React from "react";
import { FormattedMessage } from "react-intl";
import { Link } from "react-router-dom";

import { HomeFakeDisabledButton } from "./HomeFakeDisabledButton";

export const HomeCreateProductButton = () => {
const getTooltipContent = () => {
return {
reason: "",
message: "",
};
};

const canViewProducts = true;

if (!canViewProducts) {
const { message } = getTooltipContent();
const { user } = useUser();
const userPermissions = user?.userPermissions || [];
const hasPermissionToManageProducts = hasPermissions(userPermissions, [
PermissionEnum.MANAGE_PRODUCTS,
]);

if (!hasPermissionToManageProducts) {
return (
<Tooltip>
<Tooltip.Trigger>
Expand All @@ -31,23 +30,20 @@ export const HomeCreateProductButton = () => {
</Tooltip.Trigger>
<Tooltip.Content>
<Tooltip.Arrow />
{message}
<FormattedMessage
defaultMessage="You don't have permission to manage products"
id="KHI/qv"
/>
</Tooltip.Content>
</Tooltip>
);
}

const goToAllProductsUrl = productListUrl();

return (
<Button
as="a"
href={goToAllProductsUrl}
target="_blank"
rel="noreferrer noopener"
variant="primary"
>
<FormattedMessage defaultMessage="Go to all products" id="XZpRr8" description="btn label" />
</Button>
<Link to={productListUrl()}>
<Button variant="primary">
<FormattedMessage defaultMessage="Go to all products" id="XZpRr8" description="btn label" />
</Button>
</Link>
);
};
19 changes: 11 additions & 8 deletions src/newHome/homeOnboarding/HomeInviteStaffButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { staffListPath } from "@dashboard/staff/urls";
import { useUser } from "@dashboard/auth";
import { hasPermissions } from "@dashboard/components/RequirePermissions";
import { PermissionEnum } from "@dashboard/graphql";
import { staffListUrl } from "@dashboard/staff/urls";
import { Button, Tooltip } from "@saleor/macaw-ui-next";
import React from "react";
import { FormattedMessage } from "react-intl";
Expand All @@ -7,9 +10,11 @@ import { Link } from "react-router-dom";
import { HomeFakeDisabledButton } from "./HomeFakeDisabledButton";

export const HomeInviteStaffButton = () => {
const canInviteStaffMembers = true;
const { user } = useUser();
const userPermissions = user?.userPermissions || [];
const hasPermissionToManageStaff = hasPermissions(userPermissions, [PermissionEnum.MANAGE_STAFF]);

if (!canInviteStaffMembers) {
if (!hasPermissionToManageStaff) {
return (
<Tooltip>
<Tooltip.Trigger>
Expand All @@ -20,19 +25,17 @@ export const HomeInviteStaffButton = () => {
<Tooltip.Content>
<Tooltip.Arrow />
<FormattedMessage
defaultMessage="You don't have permission to invite staff members"
id="RFXT9O"
defaultMessage="You don't have permission to manage staff"
id="gt05TH"
description="tooltip message"
/>
</Tooltip.Content>
</Tooltip>
);
}

const inviteTeamMembersPath = staffListPath;

return (
<Link to={inviteTeamMembersPath}>
<Link to={staffListUrl({ action: "add" })}>
<Button variant="primary">
<FormattedMessage defaultMessage="Invite members" id="BBt3jD" description="btn label" />
</Button>
Expand Down
6 changes: 3 additions & 3 deletions src/newHome/homeOnboarding/HomeOnboarding.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DashboardCard } from "@dashboard/components/Card";
import { TOTAL_STEPS_COUNT } from "@dashboard/newHome/homeOnboarding/onboardingContext/initialOnboardingState";
import { Accordion, Box, Button, ChervonDownIcon, Text } from "@saleor/macaw-ui-next";
import * as React from "react";
import { FormattedMessage } from "react-intl";
Expand All @@ -20,13 +21,12 @@ const HomeOnboarding = () => {

const isOnboardingExpanded = onboardingState.onboardingExpanded;
const status = {
done: onboardingState.steps.filter(step => step.completed).length,
total: onboardingState.steps.length,
done: onboardingState.stepsCompleted.length,
total: TOTAL_STEPS_COUNT,
};

const handleMarkAllAsCompleted = () => {
markAllAsCompleted();
toggleOnboarding(false);
};

return (
Expand Down
2 changes: 1 addition & 1 deletion src/newHome/homeOnboarding/HomeOnboardingAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Accordion, Box, ChervonDownIcon, Skeleton, Text } from "@saleor/macaw-u
import React from "react";
import SVG from "react-inlinesvg";

import { useOnboardingData } from "./hooks/useOnboardingData";
import { useOnboarding } from "./onboardingContext/OnboardingContext";
import { useOnboardingData } from "./useOnboardingData";

export const HomeOnboardingAccordion = () => {
const { toggleExpandedOnboardingStep, extendedStepId, loading } = useOnboarding();
Expand Down
Loading

0 comments on commit 2af53a8

Please sign in to comment.