Skip to content

Commit

Permalink
feat(dashboard): Nv 4985 dashboard error state if get workflows fail …
Browse files Browse the repository at this point in the history
…we have not (#7494)
  • Loading branch information
BiswaViraj authored Jan 17, 2025
1 parent c5b5d70 commit 4b2b29b
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 192 deletions.
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/auth/auth-side-banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function AuthSideBanner() {
</div>
<div className="flex flex-col items-start justify-start gap-8 self-stretch">
<AuthFeatureRow
icon={<Plug className="h-6 w-6" />}
icon={<Plug className="h-6 w-6 text-[#DD2450]" />}
title="Powerful notifications, easy integrations"
description="Unlimited workflows, unlimited providers, unlimited subscribers with 99.9% uptime SLA"
/>
Expand Down
1 change: 0 additions & 1 deletion apps/dashboard/src/components/error-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useRouteError } from 'react-router-dom';

export default function ErrorPage() {
const error = useRouteError() as { statusText?: string; message: string };
console.error(error);

return (
<div id="error-page">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,186 +1,14 @@
import { PLAIN_SUPPORT_CHAT_APP_ID } from '@/config';
import { useAuth } from '@/context/auth/hooks';
import { useBootIntercom } from '@/hooks/use-boot-intercom';
import * as Sentry from '@sentry/react';
import { useEffect, useState } from 'react';
import { RiQuestionFill } from 'react-icons/ri';
import { HeaderButton } from './header-button';

// Add type declaration for Plain chat widget
declare global {
interface Window {
Plain?: {
init: (config: any) => void;
open: () => void;
};
}
}
import { usePlainChat } from '@/hooks/use-plain-chat';

export const CustomerSupportButton = () => {
const [isFirstRender, setIsFirstRender] = useState(true);
const { currentUser } = useAuth();

const isLiveChatVisible = currentUser?.servicesHashes?.plain && PLAIN_SUPPORT_CHAT_APP_ID !== undefined;

useBootIntercom();

useEffect(() => {
if (isFirstRender && isLiveChatVisible) {
try {
window?.Plain?.init({
appId: PLAIN_SUPPORT_CHAT_APP_ID,
hideLauncher: true,
hideBranding: true,
title: 'Chat with us',
customerDetails: {
email: currentUser?.email,
emailHash: currentUser?.servicesHashes?.plain,
externalId: currentUser?._id,
},
links: [
{
icon: 'book',
text: 'Documentation',
url: 'https://docs.novu.co?utm_campaign=in_app_live_chat',
},
{
icon: 'integration',
text: 'Roadmap',
url: 'https://roadmap.novu.co/roadmap?utm_campaign=in_app_live_chat',
},
{
icon: 'link',
text: 'Changelog',
url: 'https://roadmap.novu.co/changelog?utm_campaign=in_app_live_chat',
},
{
icon: 'email',
text: 'Contact Sales',
url: 'https://notify.novu.co/meetings/novuhq/novu-discovery-session-rr?utm_campaign=in_app_live_chat',
},
],
theme: 'light',

style: {
brandColor: '#DD2450',
launcherBackgroundColor: '#FFFFFF',
launcherIconColor: '#FFFFFF',
},

logo: {
url: 'https://dashboard.novu.co/static/images/novu.png',
alt: 'Novu Logo',
},
chatButtons: [
{
icon: 'chat',
text: 'Ask a question',
threadDetails: {
// "Question"
labelTypeIds: ['lt_01JCJ36RM5P6QSYWXPB3FNC3QF'],
},
},
{
icon: 'bulb',
text: 'Share Feedback',
threadDetails: {
// "Insight"
labelTypeIds: ['lt_01JCKS50M6D1568DSJ1Q9CHFF2'],
},
form: {
fields: [
{
type: 'dropdown',
placeholder: 'How important is this to you?',
options: [
{
icon: 'error',
text: 'Critical - Blocking my work',
threadDetails: {
labelTypeIds: ['lt_01JFYNG7N05VF956CABF23N3N8'],
},
},
{
icon: 'bulb',
text: 'Important - Should be addressed soon',
threadDetails: {
labelTypeIds: ['lt_01JFYNGRPEJ4CNA3GMYSRCRCYB'],
},
},
{
icon: 'chat',
text: 'Nice to have - Suggestion for improvement',
threadDetails: {
labelTypeIds: ['lt_01JFYNGE0EYWSE1GKAM3MTBDMC'],
},
},
],
},
],
},
},
{
icon: 'bug',
text: 'Report a bug',
form: {
fields: [
{
type: 'dropdown',
placeholder: 'Severity of the bug..',
options: [
{
icon: 'integration',
text: 'Unable to access the application',
threadDetails: {
// "Critical Severity, Bug"
labelTypeIds: ['lt_01JA88XK1N11JBBV55ZMYMEH85', 'lt_01JA88XK1N11JBBV55ZMYMEH85'],
},
},
{
icon: 'error',
text: 'Significant functionality impacted',
threadDetails: {
// "High Severity, Bug"
labelTypeIds: ['lt_01JE5V8FK3SHPR6N7XMDW8N005', 'lt_01JA88XK1N11JBBV55ZMYMEH85'],
},
},
{
icon: 'bug',
text: 'Minor inconvenience or issue',
threadDetails: {
// "Low Severity, Bug"
labelTypeIds: ['lt_01JE5V7R152BN3A9Z1R2251F1A', 'lt_01JA88XK1N11JBBV55ZMYMEH85'],
},
},
],
},
],
},
},
],
});
} catch (error) {
console.error('Error initializing plain chat: ', error);
Sentry.captureException(error);
}
}
setIsFirstRender(false);
}, [isLiveChatVisible, currentUser, isFirstRender]);
const { showPlainLiveChat } = usePlainChat();

const showPlainLiveChat = () => {
if (isLiveChatVisible) {
try {
window?.Plain?.open();
} catch (error) {
console.error('Error opening plain chat: ', error);
Sentry.captureException(error);
}
}
};
return (
<button tabIndex={-1} className="flex items-center justify-center" onClick={showPlainLiveChat}>
<HeaderButton label="Help">
<RiQuestionFill className="text-foreground-600 size-4" />{' '}
<RiQuestionFill className="text-foreground-600 size-4" />
</HeaderButton>
</button>
);
Expand Down
12 changes: 6 additions & 6 deletions apps/dashboard/src/components/icons/plug.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,47 @@ export function Plug(props: React.ComponentPropsWithoutRef<'svg'>) {
<path
id="Vector"
d="M1 24.9906L3.95687 22.0337"
stroke="#DD2450"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
id="Vector_2"
d="M5.7569 11.7485L3.95685 13.5486C2.83146 14.674 2.19922 16.2003 2.19922 17.7919C2.19922 19.3834 2.83146 20.9098 3.95685 22.0352C5.08225 23.1606 6.60861 23.7928 8.20016 23.7928C9.7917 23.7928 11.3181 23.1606 12.4435 22.0352L14.2435 20.2351L5.7569 11.7485Z"
stroke="#DD2450"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
id="Vector_3"
d="M24.9998 0.990234L22.043 3.94711"
stroke="#DD2450"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
id="Vector_4"
d="M20.2434 14.2357L22.0435 12.4356C23.1689 11.3103 23.8011 9.78389 23.8011 8.19234C23.8011 6.6008 23.1689 5.07443 22.0435 3.94904C20.9181 2.82365 19.3917 2.19141 17.8002 2.19141C16.2086 2.19141 14.6823 2.82365 13.5569 3.94904L11.7568 5.74908L20.2434 14.2357Z"
stroke="#DD2450"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
id="Vector_5"
d="M10.6003 11.7905L8.2002 14.1906"
stroke="#DD2450"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
id="Vector_6"
d="M14.1999 15.3906L11.7998 17.7907"
stroke="#DD2450"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
Expand Down
44 changes: 44 additions & 0 deletions apps/dashboard/src/components/shared/server-error-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { RiQuestionAnswerLine } from 'react-icons/ri';
import { Button } from '../primitives/button';
import { Plug } from '../icons/plug';
import { usePlainChat } from '@/hooks/use-plain-chat';

export function ServerErrorPage() {
const { showPlainLiveChat } = usePlainChat();

return (
<div className="peer flex h-full w-full flex-col items-center justify-center" data-error="true">
<div className="relative flex w-3/4 flex-col items-center justify-center gap-3">
<div className="absolute inset-0 -z-50 h-full w-full rounded-[866px] border border-dashed border-[#E7E7E7] bg-[#E7E7E7] blur-[220px]" />
<div className="flex w-40 items-center gap-3 rounded-md border border-[#e6e6e6] bg-white px-3 py-4 shadow-[0px_4.233px_4.233px_0px_rgba(31,40,55,0.02),0px_1.693px_1.693px_0px_rgba(31,40,55,0.02),0px_-3px_0px_0px_#F7F7F7_inset]">
<span className="size-3 rounded-full bg-[#e6e6e6]" />
<span className="h-3 flex-1 rounded-md bg-[#e6e6e6]" />
</div>
<Plug className="size-4 text-[#E6E6E6]" />
<div className="w-40 rounded-md border border-[#e6e6e6] bg-white px-3 py-1 text-center shadow-[0px_4.233px_4.233px_0px_rgba(31,40,55,0.02),0px_1.693px_1.693px_0px_rgba(31,40,55,0.02),0px_-3px_0px_0px_#F7F7F7_inset]">
<p className="text-2xl font-extrabold text-[#ebecef]">500</p>
</div>
<div className="mt-6 flex flex-col items-center gap-3">
<p className="font-medium text-gray-900">Uh-oh, this is on us, not you.</p>
<div>
<p className="text-text-soft text-center text-xs font-medium">
Whoops, we missed a beat. This 500 is a reminder
</p>
<p className="text-text-soft text-center text-xs font-medium">we're still human… mostly.</p>
</div>

<Button
leadingIcon={RiQuestionAnswerLine}
size="sm"
variant="secondary"
mode="outline"
className="mt-3"
onClick={showPlainLiveChat}
>
Get in Touch
</Button>
</div>
</div>
</div>
);
}
3 changes: 2 additions & 1 deletion apps/dashboard/src/components/workflow-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { WorkflowRow } from '@/components/workflow-row';
import { useFetchWorkflows } from '@/hooks/use-fetch-workflows';
import { RiMore2Fill } from 'react-icons/ri';
import { createSearchParams, useLocation, useSearchParams } from 'react-router-dom';
import { ServerErrorPage } from './shared/server-error-page';

export function WorkflowList() {
const [searchParams] = useSearchParams();
Expand All @@ -34,7 +35,7 @@ export function WorkflowList() {
offset,
});

if (isError) return null;
if (isError) return <ServerErrorPage />;

if (!isPending && data.totalCount === 0) {
return <WorkflowListEmpty />;
Expand Down
Loading

0 comments on commit 4b2b29b

Please sign in to comment.