Skip to content
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
7474935
show alert if there are failed/pending deliveries
Cloud11PL Feb 12, 2025
eea1f00
i18n
Cloud11PL Feb 12, 2025
93a4613
useMemo and booleans
Cloud11PL Feb 12, 2025
18ade3f
get latest failed/pending event
Cloud11PL Feb 12, 2025
b54b6f7
check attempts
Cloud11PL Feb 13, 2025
f2d5143
add feature flag
Cloud11PL Feb 13, 2025
b1b9be9
cr fixes
Cloud11PL Feb 13, 2025
098aff4
fix test
Cloud11PL Feb 13, 2025
433420b
filter by thirdparty
Cloud11PL Feb 13, 2025
379e926
hook refactor
Cloud11PL Feb 13, 2025
651fab7
add missing test
Cloud11PL Feb 13, 2025
6f272f3
generate flag
Cloud11PL Feb 17, 2025
be4f48b
generate flag
Cloud11PL Feb 17, 2025
35781a9
add feature flag
Cloud11PL Feb 13, 2025
a8a79a2
cr fixes
Cloud11PL Feb 13, 2025
f0dd22a
generate flag
Cloud11PL Feb 17, 2025
7bd087d
generate flag
Cloud11PL Feb 17, 2025
f2f15fd
add feature flag
Cloud11PL Feb 13, 2025
d7e0b8d
cr fixes
Cloud11PL Feb 13, 2025
7a6a855
apps list ui change
Cloud11PL Feb 13, 2025
ee5020e
show icon when app is disabled
Cloud11PL Feb 14, 2025
0afdc38
app list alerts
Cloud11PL Feb 14, 2025
63340e3
row alert
Cloud11PL Feb 17, 2025
ac7b7cf
link to app details
Cloud11PL Feb 17, 2025
e600783
show focused and hover
Cloud11PL Feb 17, 2025
be86ccd
add alert for disabled app
Cloud11PL Feb 17, 2025
0a41358
add tests
Cloud11PL Feb 17, 2025
32c94ab
changeset
Cloud11PL Feb 17, 2025
f0b48af
fix permission check
Cloud11PL Feb 17, 2025
9bbdf3e
sort installed apps
Cloud11PL Feb 17, 2025
cf4d58b
add more tests
Cloud11PL Feb 17, 2025
e5f5ebd
fix tests
Cloud11PL Feb 17, 2025
c4b4a69
i18n
Cloud11PL Feb 17, 2025
27f99b8
Merge branch 'main' into PROD-99-sidebar-apps-alert
Cloud11PL Feb 18, 2025
fef7288
Merge branch 'PROD-99-sidebar-apps-alert' into PROD-100-app-list-apps…
Cloud11PL Feb 18, 2025
bb1ead6
hide issues alert behind feature flag
Cloud11PL Feb 18, 2025
03c490d
mock use flag in test
Cloud11PL Feb 18, 2025
cbb86c7
Merge branch 'main' into PROD-100-app-list-apps-alert
Cloud11PL Feb 19, 2025
ffc06e7
fix icon import
Cloud11PL Feb 19, 2025
aa9be82
fix feature flag usage
Cloud11PL Feb 19, 2025
bdb1499
fetch more pending deliveries
Cloud11PL Feb 19, 2025
9fb2116
remove app sorting by issue
Cloud11PL Feb 20, 2025
3276d98
fix date formatting
Cloud11PL Feb 20, 2025
0ce6c60
Merge branch 'main' into PROD-100-app-list-apps-alert
Cloud11PL Feb 20, 2025
d965e60
don't fetch webhooks without permissions
Cloud11PL Feb 20, 2025
703061f
Merge branch 'main' into PROD-100-app-list-apps-alert
Cloud11PL Feb 21, 2025
f734b0d
cr fixes
Cloud11PL Feb 21, 2025
5ac8918
fix custom app list query
Cloud11PL Feb 21, 2025
8df6b09
Merge branch 'main' into PROD-100-app-list-apps-alert
Cloud11PL Feb 21, 2025
608a8f1
Merge branch 'main' into PROD-100-app-list-apps-alert
Cloud11PL Feb 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/hip-bobcats-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"saleor-dashboard": patch
---

App list now shows an alert if app's webhooks have failed or the app is disabled. This means you can see if there are issues with your apps without having to enter app details.

12 changes: 6 additions & 6 deletions .featureFlags/generated.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// @ts-nocheck

import X82432 from "./images/app-alerts.jpg"
import J50320 from "./images/discounts-list.png"
import K65771 from "./images/improved_refunds.png"
import M09811 from "./images/app-alerts.jpg"
import T88459 from "./images/discounts-list.png"
import L79851 from "./images/improved_refunds.png"

const app_alerts = () => (<><p><img src={X82432} alt="new filters"/>
const app_alerts = () => (<><p><img src={M09811} alt="new filters"/>
Experience new notifications displaying alerts for apps in the Dashboard.
Get meaningful information when Saleor detects issues with an app.</p>
</>)
const discounts_rules = () => (<><p><img src={J50320} alt="Discount rules"/></p>
const discounts_rules = () => (<><p><img src={T88459} alt="Discount rules"/></p>
<p>Apply the new discounts rules to narrow your promotions audience.
Set up conditions and channels that must be fulfilled to apply defined reward.</p>
</>)
const improved_refunds = () => (<><p><img src={K65771} alt="Improved refunds"/></p>
const improved_refunds = () => (<><p><img src={L79851} alt="Improved refunds"/></p>
<h3 id="enable-the-enhanced-refund-feature-to-streamline-your-refund-process">Enable the enhanced refund feature to streamline your refund process:</h3>
<ul>
<li><p>• Choose between automatic calculations based on selected items or enter refund amounts directly for overcharges and custom adjustments.</p>
Expand Down
15 changes: 15 additions & 0 deletions locale/defaultMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2697,6 +2697,9 @@
"FZTrzW": {
"string": "Manual refund"
},
"FaRg9/": {
"string": "Webhook errors detected. Last occurred at {date}. {viewDetails}."
},
"Fbr4Vp": {
"context": "dialog header",
"string": "Permissions"
Expand Down Expand Up @@ -3143,6 +3146,9 @@
"HvJPcU": {
"string": "Category deleted"
},
"HwHSeY": {
"string": "App disabled"
},
"I+1KzL": {
"context": "tab name",
"string": "All attributes"
Expand Down Expand Up @@ -3863,6 +3869,9 @@
"context": "table header warehouse stock label",
"string": "Warehouse stock"
},
"MnpUD7": {
"string": "View details"
},
"MpR4zK": {
"context": "customer details, header",
"string": "{fullName} Details"
Expand Down Expand Up @@ -8128,6 +8137,9 @@
"context": "order status",
"string": "Unfulfilled"
},
"oCrEdS": {
"string": "Activate the app from the settings. {viewDetails}."
},
"oHbgcK": {
"context": "PageTypeDeleteWarningDialog title",
"string": "Delete page {selectedTypesCount,plural,one{type} other{types}}"
Expand Down Expand Up @@ -8798,6 +8810,9 @@
"context": "voucher status",
"string": "Expired"
},
"t9sWqJ": {
"string": "Issues found"
},
"tA5HJx": {
"context": "webhook input help text",
"string": "secret key is used to create a hash signature with each payload. *optional field"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const AppAdditionalInfo: React.FC<{
<Tooltip>
<Tooltip.Trigger>
<Box display="flex" placeItems="center">
<InfoIcon color="default2" size="large" />
<InfoIcon color="default2" size="medium" />
</Box>
</Tooltip.Trigger>
<Tooltip.Content side="bottom">
Expand Down
21 changes: 21 additions & 0 deletions src/apps/components/AppAlerts/AlertExclamationIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { WARNING_ICON_COLOR, WARNING_ICON_COLOR_LIGHTER } from "@dashboard/colors";
import { ExclamationIcon } from "@dashboard/icons/ExclamationIcon";
import { ExclamationIconFilled } from "@dashboard/icons/ExclamationIconFilled";
import { Box } from "@saleor/macaw-ui-next";
import React, { useState } from "react";

export const AlertExclamationIcon = () => {
const [isHovered, setIsHovered] = useState(false);

return (
<Box
__color={isHovered ? WARNING_ICON_COLOR_LIGHTER : WARNING_ICON_COLOR}
__width={17}
__height={17}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}

Check warning on line 16 in src/apps/components/AppAlerts/AlertExclamationIcon.tsx

View check run for this annotation

Codecov / codecov/patch

src/apps/components/AppAlerts/AlertExclamationIcon.tsx#L15-L16

Added lines #L15 - L16 were not covered by tests
>
{isHovered ? <ExclamationIconFilled /> : <ExclamationIcon />}
</Box>
);
};
59 changes: 59 additions & 0 deletions src/apps/components/AppAlerts/AppRowDisabledAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { AppPaths } from "@dashboard/apps/urls";
import Link from "@dashboard/components/Link";
import { StopPropagation } from "@dashboard/components/StopPropagation";
import { AppListItemFragment } from "@dashboard/graphql";
import { DisabledIcon } from "@dashboard/icons/Disabled";
import { Box, Text, Tooltip } from "@saleor/macaw-ui-next";
import React from "react";
import { FormattedMessage } from "react-intl";

interface AppRowDisabledAlertProps {
app: AppListItemFragment;
}

export const AppRowDisabledAlert = ({ app }: AppRowDisabledAlertProps) => {
if (app.isActive) {
return null;
}

const detailsLink = AppPaths.resolveAppDetailsPath(app.id);

Check warning on line 19 in src/apps/components/AppAlerts/AppRowDisabledAlert.tsx

View check run for this annotation

Codecov / codecov/patch

src/apps/components/AppAlerts/AppRowDisabledAlert.tsx#L19

Added line #L19 was not covered by tests

return (
<Tooltip>
<Tooltip.Trigger>
<Box color="default2" __transform="scale(0.8)">
<DisabledIcon />
</Box>
</Tooltip.Trigger>

<Tooltip.Content align="start" side="bottom">
<StopPropagation>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: why do you use StopPropagation ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the event is transffered to the app list row and when you click on the link inside the tooltip you would get navigated to the app instead of the link.

<Box display="flex" flexDirection="row" gap={2} color="default2">
<Box __transform="scale(0.8)" __marginTop="-3px">
<DisabledIcon />
</Box>

<Box display="flex" flexDirection="column" gap={1}>
<Text fontSize={5} fontWeight="bold">
<FormattedMessage defaultMessage="App disabled" id="HwHSeY" />
</Text>
<Text>
<FormattedMessage
defaultMessage="Activate the app from the settings. {viewDetails}."
id="oCrEdS"
values={{
viewDetails: (
<Link href={detailsLink} color="secondary" underline>
<FormattedMessage defaultMessage="View details" id="MnpUD7" />
</Link>
),
}}
/>
</Text>
</Box>
</Box>
</StopPropagation>
</Tooltip.Content>
</Tooltip>
);
};
69 changes: 69 additions & 0 deletions src/apps/components/AppAlerts/AppRowWebhookIssueAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { AppPaths } from "@dashboard/apps/urls";
import EventTime from "@dashboard/components/EventTime";
import Link from "@dashboard/components/Link";
import { StopPropagation } from "@dashboard/components/StopPropagation";
import { AppListItemFragment } from "@dashboard/graphql";
import { ExclamationIcon } from "@dashboard/icons/ExclamationIcon";
import { Box, Text, Tooltip } from "@saleor/macaw-ui-next";
import React, { useMemo } from "react";
import { FormattedMessage } from "react-intl";

import { AlertExclamationIcon } from "../AppAlerts/AlertExclamationIcon";
import { getLatestFailedAttemptFromWebhooks } from "./utils";

interface AppRowWebhookIssueAlertProps {
app: AppListItemFragment;
}

export const AppRowWebhookIssueAlert = ({ app }: AppRowWebhookIssueAlertProps) => {
const latestFailedAttempt = useMemo(
() => app.webhooks && getLatestFailedAttemptFromWebhooks(app.webhooks),
[app],
);

if (!latestFailedAttempt) {
return null;
}

const detailsLink = AppPaths.resolveAppDetailsPath(app.id);

return (
<Tooltip>
<Tooltip.Trigger>
<Box data-test-id="app-warning-dot">
<AlertExclamationIcon />
</Box>
</Tooltip.Trigger>

<Tooltip.Content align="start" side="bottom">
<StopPropagation>
<Box display="flex" flexDirection="row" gap={2} __color="#FFB84E">
<Box marginTop={0.5}>
<ExclamationIcon />
</Box>

<Box display="flex" flexDirection="column" gap={1}>
<Text fontSize={5} fontWeight="bold">
<FormattedMessage defaultMessage="Issues found" id="t9sWqJ" />
</Text>
<Text>
<FormattedMessage
defaultMessage="Webhook errors detected. Last occurred at {date}. {viewDetails}."
id="FaRg9/"
values={{
date: <EventTime date={latestFailedAttempt.createdAt} />,
viewDetails: (
<Link href={detailsLink} color="secondary" underline>
<FormattedMessage defaultMessage="View details" id="MnpUD7" />
</Link>
),
}}
/>
</Text>
</Box>
</Box>
</StopPropagation>
</Tooltip.Content>
</Tooltip>
);
};
26 changes: 4 additions & 22 deletions src/apps/components/AppAlerts/SidebarAppAlert.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,12 @@
import { AppSections } from "@dashboard/apps/urls";
import { WARNING_ICON_COLOR, WARNING_ICON_COLOR_LIGHTER } from "@dashboard/colors";
import { ExclamationIcon } from "@dashboard/icons/ExclamationIcon";
import { ExclamationIconFilled } from "@dashboard/icons/ExclamationIconFilled";
import { Box, Text, Tooltip } from "@saleor/macaw-ui-next";
import React, { useState } from "react";
import { Text, Tooltip } from "@saleor/macaw-ui-next";
import React from "react";
import { FormattedMessage } from "react-intl";
import { Link } from "react-router-dom";

import { AlertExclamationIcon } from "./AlertExclamationIcon";
import { useAppsAlert } from "./useAppsAlert";

const ExclamationIconComponent = () => {
const [isHovered, setIsHovered] = useState(false);

return (
<Box
__color={isHovered ? WARNING_ICON_COLOR_LIGHTER : WARNING_ICON_COLOR}
__width={17}
__height={17}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{isHovered ? <ExclamationIconFilled /> : <ExclamationIcon />}
</Box>
);
};

export const SidebarAppAlert = () => {
const { hasFailedAttempts } = useAppsAlert();

Expand All @@ -36,7 +18,7 @@ export const SidebarAppAlert = () => {
<Tooltip>
<Tooltip.Trigger>
<Link to={AppSections.appsSection}>
<ExclamationIconComponent />
<AlertExclamationIcon />
</Link>
</Tooltip.Trigger>

Expand Down
33 changes: 2 additions & 31 deletions src/apps/components/AppAlerts/queries.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,11 @@
import { gql } from "@apollo/client";

export const appFailedPendingWebhooks = gql`
query AppFailedPendingWebhooks {
query AppFailedPendingWebhooks($canFetchAppEvents: Boolean!) {
apps(first: 50, filter: { type: THIRDPARTY }) {
edges {
node {
webhooks {
failedDelivers: eventDeliveries(
first: 1
filter: { status: FAILED }
sortBy: { field: CREATED_AT, direction: DESC }
) {
edges {
node {
id
}
}
}
pendingDelivers: eventDeliveries(
first: 1
filter: { status: PENDING }
sortBy: { field: CREATED_AT, direction: DESC }
) {
edges {
node {
attempts(first: 6, sortBy: { field: CREATED_AT, direction: DESC }) {
edges {
node {
status
}
}
}
}
}
}
}
...AppEventDeliveries
}
}
}
Expand Down
Loading
Loading