Skip to content

Commit

Permalink
Add contextual links to some list views and Playground panel (#5312)
Browse files Browse the repository at this point in the history
* add contextual links

* contextual links

* add tracking

* changesets

* i18n fix

* remove fragment

* type fix

* small UI fixes

---------

Co-authored-by: Paweł Chyła <chyla1988@gmail.com>
  • Loading branch information
Cloud11PL and poulch committed Dec 11, 2024
1 parent fb56837 commit e964e95
Show file tree
Hide file tree
Showing 15 changed files with 298 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-flowers-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

You can now see contextual links to documentation in product, webhooks, order, staff members lists and Graphql Playground panel.
33 changes: 33 additions & 0 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,9 @@
"19/lwV": {
"string": "Determine attributes used to create product types"
},
"19o9WS": {
"string": "Learn more about {userPermissions}"
},
"1BNKCZ": {
"context": "sale channel",
"string": "Channel"
Expand Down Expand Up @@ -1134,6 +1137,9 @@
"context": "vat not included in order price",
"string": "does not apply"
},
"5M+hLZ": {
"string": "User permissions"
},
"5O8EIz": {
"context": "Authorize transactions instead of charging",
"string": "Authorize transactions instead of charging"
Expand Down Expand Up @@ -1616,6 +1622,9 @@
"context": "service accounts section name",
"string": "Service Accounts"
},
"91vQMc": {
"string": "order management"
},
"945a4a": {
"context": "column header",
"string": "Customer e-mail"
Expand Down Expand Up @@ -1965,9 +1974,15 @@
"context": "dialog header",
"string": "Unassign Categories From Sale"
},
"B8PvdI": {
"string": "extending Saleor with Webhooks"
},
"B9yrkK": {
"string": "No Channels found"
},
"BA4leV": {
"string": "API guide"
},
"BBt3jD": {
"context": "btn label",
"string": "Invite members"
Expand Down Expand Up @@ -2125,6 +2140,9 @@
"context": "error with deactivatation alert message",
"string": "Errors deactivating gift {count,plural,one{card} other{cards}}"
},
"C9cgck": {
"string": "Learn more about {productConfigurations}"
},
"C9pcQx": {
"context": "dialog content",
"string": "{counter,plural,one{Are you sure you want to delete this shipping zone?} other{Are you sure you want to delete {displayQuantity} shipping zones?}}"
Expand Down Expand Up @@ -5892,6 +5910,9 @@
"context": "note input subtitle",
"string": "Why was this gift card issued. This note will not be shown to the customer. Note will be stored in gift card history"
},
"Zvo5iu": {
"string": "API reference"
},
"Zz67wc": {
"string": "View and update your webhooks and events."
},
Expand Down Expand Up @@ -6469,6 +6490,9 @@
"context": "edit tracking button",
"string": "Edit tracking"
},
"dVk241": {
"string": "product configurations"
},
"dWK/Ck": {
"string": "Choose countries, you want voucher to be limited to, from the list below"
},
Expand Down Expand Up @@ -8749,6 +8773,9 @@
"tiY7bx": {
"string": "Add new product"
},
"tl1/1E": {
"string": "Learn more about {orderManagement}"
},
"tlGXkh": {
"context": "input description",
"string": "Unlimited"
Expand Down Expand Up @@ -9643,6 +9670,9 @@
"context": "select all channels label",
"string": "Select All Channels"
},
"zRIsjN": {
"string": "Learn more about {apiReference} and view {apiGuide}"
},
"zRrcOG": {
"context": "order history message",
"string": "Order was cancelled"
Expand All @@ -9653,6 +9683,9 @@
"zSOvI0": {
"string": "Filters"
},
"zT1CvH": {
"string": "Learn more about {extendingSaleor}"
},
"zWM89r": {
"context": "numeric attribute units of",
"string": "Units of"
Expand Down
41 changes: 41 additions & 0 deletions src/components/AppLayout/ContextualLinks/ContextualLine.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Box, Paragraph, TextProps } from "@saleor/macaw-ui-next";
import React from "react";

const Root = ({ children, ...rest }: TextProps) => (
<Paragraph
width="100%"
color="default2"
fontWeight="medium"
fontSize={2}
paddingBottom={2}
paddingTop={1}
{...rest}
>
{children}
</Paragraph>
);

const ContextualLineLink = ({
href,
children,
onClick,
}: {
href: string;
children: React.ReactNode;
onClick?: () => void;
}) => (
<Box
as="a"
textDecoration="underline"
href={href}
target="_blank"
rel="noopener noreferrer"
onClick={onClick}
>
{children}
</Box>
);

export const ContextualLine = Object.assign(Root, {
Link: ContextualLineLink,
});
48 changes: 48 additions & 0 deletions src/components/AppLayout/ContextualLinks/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { defineMessages } from "react-intl";

export const contextualLinks = defineMessages({
devModePanel: {
defaultMessage: "Learn more about {apiReference} and view {apiGuide}",
id: "zRIsjN",
},
apiReference: {
defaultMessage: "API reference",
id: "Zvo5iu",
},
apiGuide: {
defaultMessage: "API guide",
id: "BA4leV",
},
webhooks: {
defaultMessage: "Learn more about {extendingSaleor}",
id: "zT1CvH",
},
extendingSaleor: {
defaultMessage: "extending Saleor with Webhooks",
id: "B8PvdI",
},
staffMembers: {
defaultMessage: "Learn more about {userPermissions}",
id: "19o9WS",
},
userPermissions: {
defaultMessage: "User permissions",
id: "5M+hLZ",
},
orders: {
defaultMessage: "Learn more about {orderManagement}",
id: "tl1/1E",
},
orderManagement: {
defaultMessage: "order management",
id: "91vQMc",
},
products: {
defaultMessage: "Learn more about {productConfigurations}",
id: "C9cgck",
},
productConfigurations: {
defaultMessage: "product configurations",
id: "dVk241",
},
});
95 changes: 95 additions & 0 deletions src/components/AppLayout/ContextualLinks/useContextualLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { useAnalytics } from "@dashboard/components/ProductAnalytics/useAnalytics";
import {
API_GUIDE_DOCS,
API_REFERENCE_DOCS,
EXTENDING_WITH_WEBHOOKS_DOCS_URL,
ORDER_MANAGEMENT_DOCS_URL,
PRODUCT_CONFIGURATION_DOCS_URL,
USER_PERMISSIONS_DOCS_URL,
} from "@dashboard/links";
import * as React from "react";
import { useIntl } from "react-intl";

import { ContextualLine } from "./ContextualLine";
import { contextualLinks } from "./messages";

type SubtitleType =
| "extending_saleor"
| "product_list"
| "order_list"
| "dev_panel"
| "webhooks_events"
| "staff_members";

const EVENT = "contextual_link_clicked";

export const useContextualLink = (type: SubtitleType) => {
const { trackEvent: track } = useAnalytics();
const intl = useIntl();
const trackEvent = (type: string) => track(EVENT, { type });

switch (type) {
case "staff_members":
return intl.formatMessage(contextualLinks.staffMembers, {
userPermissions: (
<ContextualLine.Link
href={USER_PERMISSIONS_DOCS_URL}
onClick={() => trackEvent("user_permissions_docs")}
>
{intl.formatMessage(contextualLinks.userPermissions)}
</ContextualLine.Link>
),
});
case "extending_saleor":
return intl.formatMessage(contextualLinks.webhooks, {
extendingSaleor: (
<ContextualLine.Link
href={EXTENDING_WITH_WEBHOOKS_DOCS_URL}
onClick={() => trackEvent("extending_saleor_docs")}
>
{intl.formatMessage(contextualLinks.extendingSaleor)}
</ContextualLine.Link>
),
});
case "dev_panel":
return intl.formatMessage(contextualLinks.devModePanel, {
apiReference: (
<ContextualLine.Link
href={API_REFERENCE_DOCS}
onClick={() => trackEvent("api_reference_docs")}
>
{intl.formatMessage(contextualLinks.apiReference)}
</ContextualLine.Link>
),
apiGuide: (
<ContextualLine.Link href={API_GUIDE_DOCS} onClick={() => trackEvent("api_guide_docs")}>
{intl.formatMessage(contextualLinks.apiGuide)}
</ContextualLine.Link>
),
});
case "order_list":
return intl.formatMessage(contextualLinks.orders, {
orderManagement: (
<ContextualLine.Link
href={ORDER_MANAGEMENT_DOCS_URL}
onClick={() => trackEvent("order_management_docs")}
>
{intl.formatMessage(contextualLinks.orderManagement)}
</ContextualLine.Link>
),
});
case "product_list":
return intl.formatMessage(contextualLinks.products, {
productConfigurations: (
<ContextualLine.Link
href={PRODUCT_CONFIGURATION_DOCS_URL}
onClick={() => trackEvent("product_configuration_docs")}
>
{intl.formatMessage(contextualLinks.productConfigurations)}
</ContextualLine.Link>
),
});
default:
return null;
}
};
43 changes: 29 additions & 14 deletions src/components/AppLayout/TopNav/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ import React, { PropsWithChildren } from "react";

import useAppChannel from "../AppChannelContext";
import AppChannelSelect from "../AppChannelSelect";
import { ContextualLine } from "../ContextualLinks/ContextualLine";
import { TopNavLink } from "./TopNavLink";
import { TopNavWrapper } from "./TopNavWrapper";

interface TopNavProps {
title: string | React.ReactNode;
subtitle?: React.ReactNode;
href?: string;
withoutBorder?: boolean;
isAlignToRight?: boolean;
}

export const Root: React.FC<PropsWithChildren<TopNavProps>> = ({
title,
subtitle,
href,
withoutBorder = false,
isAlignToRight = true,
Expand All @@ -26,21 +29,33 @@ export const Root: React.FC<PropsWithChildren<TopNavProps>> = ({
const channels = user?.user?.accessibleChannels ?? [];

return (
<TopNavWrapper withoutBorder={withoutBorder}>
{href && <TopNavLink to={href} />}
<Box __flex={isAlignToRight ? 1 : 0} __minWidth="max-content">
<Text size={6}>{title}</Text>
</Box>
<Box display="flex" flexWrap="nowrap" height="100%" __flex={isAlignToRight ? "initial" : 1}>
{isPickerActive && channels.length > 0 && (
<AppChannelSelect
channels={channels}
selectedChannelId={channel?.id}
onChannelSelect={setChannel}
/>
)}
{children}
<TopNavWrapper withoutBorder={withoutBorder} hasSubtitle={!!subtitle}>
<Box display="flex" alignItems="center" width="100%">
{href && <TopNavLink to={href} />}
<Box __flex={isAlignToRight ? 1 : 0} __minWidth="max-content">
<Text size={6}>{title}</Text>
</Box>
<Box display="flex" flexWrap="nowrap" height="100%" __flex={isAlignToRight ? "initial" : 1}>
{isPickerActive && channels.length > 0 && (
<AppChannelSelect
channels={channels}
selectedChannelId={channel?.id}
onChannelSelect={setChannel}
/>
)}
{children}
</Box>
</Box>
{subtitle ? (
<ContextualLine
gridColumn="8"
// The subtitle should be aligned with the title, not back button
marginLeft={href ? 12 : 0}
__marginTop={href ? "-0.6rem" : 0}
>
{subtitle}
</ContextualLine>
) : null}
</TopNavWrapper>
);
};
9 changes: 4 additions & 5 deletions src/components/AppLayout/TopNav/TopNavWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import { Box } from "@saleor/macaw-ui-next";
import React from "react";

import { topBarHeight } from "../consts";
import { topBarHeight, topBarHeightSubtitle } from "../consts";

export const TopNavWrapper: React.FC<{ withoutBorder?: boolean }> = ({
export const TopNavWrapper: React.FC<{ withoutBorder?: boolean; hasSubtitle?: boolean }> = ({
children,
withoutBorder,
hasSubtitle,
}) => (
<Box
display="flex"
alignItems="center"
paddingX={6}
paddingY={5}
borderBottomWidth={withoutBorder ? 0 : 1}
borderBottomStyle="solid"
borderColor="default1"
position="relative"
data-test-id="page-header"
__height={topBarHeight}
__height={hasSubtitle ? topBarHeightSubtitle : topBarHeight}
gridColumn="8"
gridRowStart="1"
backgroundColor="default1"
Expand Down
Loading

0 comments on commit e964e95

Please sign in to comment.