Skip to content

Commit

Permalink
chore(root): Release 2024-11-26 17:36 (#7135)
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored Nov 26, 2024
2 parents b6dc0a8 + eea13a4 commit 7ae8769
Show file tree
Hide file tree
Showing 19 changed files with 246 additions and 196 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { MessageRepository, SubscriberRepository } from '@novu/dal';
import { ChannelTypeEnum } from '@novu/shared';
import { buildMessageCountKey, CachedQuery } from '@novu/application-generic';
import { buildMessageCountKey, CachedQuery, InstrumentUsecase } from '@novu/application-generic';

import { GetFeedCountCommand } from './get-feed-count.command';
import { ApiException } from '../../../shared/exceptions/api.exception';
Expand All @@ -13,6 +13,7 @@ export class GetFeedCount {
private subscriberRepository: SubscriberRepository
) {}

@InstrumentUsecase()
@CachedQuery({
builder: ({ environmentId, subscriberId, ...command }: GetFeedCountCommand) =>
buildMessageCountKey().cache({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
buildSubscriberKey,
CachedEntity,
CachedQuery,
InstrumentUsecase,
} from '@novu/application-generic';
import { MessageRepository, SubscriberEntity, SubscriberRepository } from '@novu/dal';

Expand Down Expand Up @@ -33,6 +34,7 @@ export class GetNotificationsFeed {
}
}

@InstrumentUsecase()
@CachedQuery({
builder: ({ environmentId, subscriberId, ...command }: GetNotificationsFeedCommand) =>
buildFeedKey().cache({
Expand Down
1 change: 1 addition & 0 deletions apps/dashboard/public/images/auth/ui-org.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions apps/dashboard/src/components/auth/auth-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Card } from '../primitives/card';

export function AuthCard({ children }: { children: React.ReactNode }) {
return <Card className="flex h-[692px] w-full overflow-hidden">{children}</Card>;
}
45 changes: 45 additions & 0 deletions apps/dashboard/src/components/auth/create-organization.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { OrganizationList as OrganizationListForm } from '@clerk/clerk-react';
import { ROUTES } from '../../utils/routes';
import { clerkSignupAppearance } from '../../utils/clerk-appearance';
import { AuthCard } from './auth-card';
import { RiArrowLeftSLine } from 'react-icons/ri';

export default function OrganizationCreate() {
return (
<div className="mx-auto flex w-full max-w-[1130px] flex-col gap-3">
<AuthCard>
<div className="flex min-w-[564px] max-w-[564px] items-center p-[60px]">
<div className="flex flex-col gap-[4px]">
<StepIndicator />
<OrganizationListForm
appearance={{
elements: {
...clerkSignupAppearance.elements,
cardBox: { boxShadow: 'none' },
card: { paddingTop: 0, padding: 0 },
},
}}
hidePersonal
skipInvitationScreen
afterSelectOrganizationUrl={ROUTES.ENV}
afterCreateOrganizationUrl={ROUTES.ENV}
/>
</div>
</div>

<div className="w-full max-w-[564px] flex-1">
<img src="/images/auth/ui-org.svg" alt="create-org-illustration" className="opacity-70" />
</div>
</AuthCard>
</div>
);
}

function StepIndicator(): JSX.Element {
return (
<div className="text-foreground-600 inline-flex items-center gap-[2px] pl-[20px]">
<RiArrowLeftSLine className="h-4 w-4" />
<span className="font-label-x-small text-xs">1/3</span>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import { ReactNode } from 'react';
import { Tooltip, TooltipContent, TooltipTrigger } from '../primitives/tooltip';
import { cn } from '@/utils/ui';

export const HeaderButton = ({
children,
label,
disableTooltip = false,
className,
}: {
children: ReactNode;
label: ReactNode;
disableTooltip?: boolean;
className?: string;
}) => {
return (
<Tooltip>
<TooltipTrigger asChild>
<div
tabIndex={0}
className="hover:bg-foreground-100 focus-visible:ring-ring flex h-6 w-6 cursor-pointer items-center justify-center rounded-2xl transition-[background-color,box-shadow] duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2"
className={cn(
`hover:bg-foreground-100 focus-visible:ring-ring flex h-6 w-6 cursor-pointer items-center justify-center rounded-2xl transition-[background-color,box-shadow] duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2`,
className
)}
>
{children}
</div>
Expand Down
17 changes: 12 additions & 5 deletions apps/dashboard/src/components/icons/inbox-bell-filled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,23 @@ import type { HTMLAttributes } from 'react';
type InboxBellFilledProps = {
bellClassName?: string;
ringerClassName?: string;
codeClassName?: string;
};

export function InboxBellFilled(props: HTMLAttributes<HTMLOrSVGElement> & InboxBellFilledProps) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 9 12" {...props}>
<path
className={props.bellClassName}
fill="currentColor"
d="M4.5.856a.642.642 0 0 0-.643.643v.386a3.216 3.216 0 0 0-2.572 3.15v.377c0 .945-.347 1.857-.974 2.564l-.149.167a.642.642 0 0 0 .48 1.07h7.715a.644.644 0 0 0 .48-1.07l-.149-.167a3.863 3.863 0 0 1-.974-2.564v-.377a3.216 3.216 0 0 0-2.572-3.15v-.386A.642.642 0 0 0 4.5.856Z"
></path>
<g className={props.bellClassName}>
<path
fill="currentColor"
d="M4.5.856a.642.642 0 0 0-.643.643v.386a3.216 3.216 0 0 0-2.572 3.15v.377c0 .945-.347 1.857-.974 2.564l-.149.167a.642.642 0 0 0 .48 1.07h7.715a.644.644 0 0 0 .48-1.07l-.149-.167a3.863 3.863 0 0 1-.974-2.564v-.377a3.216 3.216 0 0 0-2.572-3.15v-.386A.642.642 0 0 0 4.5.856Z"
></path>
<path
className={props.codeClassName}
fill="white"
d="M7.2 6.46 5.9272 7.7328 5.609 7.4146 6.5636 6.46 5.609 5.5054 5.9272 5.1872 7.2 6.46ZM2.4364 6.46 3.391 7.4146 3.0728 7.7328 1.8 6.46 3.0728 5.1872 3.391 5.5054 2.4364 6.46ZM4.0024 8.485H3.5236L4.9976 4.435H5.4764L4.0024 8.485Z"
></path>
</g>
<path
className={props.ringerClassName}
fill="currentColor"
Expand Down
54 changes: 47 additions & 7 deletions apps/dashboard/src/components/inbox-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,54 @@ import { Bell, Inbox, InboxContent, useNovu } from '@novu/react';
import { InboxBellFilled } from './icons/inbox-bell-filled';
import { HeaderButton } from './header-navigation/header-button';
import { useState, useEffect } from 'react';
import { useEnvironment } from '../context/environment/hooks';
import { useTestPage } from '@/hooks/use-test-page';

const InboxInner = () => {
const [open, setOpen] = useState(false);
const [jingle, setJingle] = useState(false);
const { isTestPage } = useTestPage();

const novu = useNovu();
useEffect(() => {
// Store a timeout to debounce the jingle animation, preventing the bell from
// becoming jittery when multiple notifications are received in quick succession.
let timeout: NodeJS.Timeout;

const cleanup = novu.on('notifications.notification_received', () => {
setJingle(true);
const timeout = setTimeout(() => setJingle(false), 3000);

return () => clearTimeout(timeout);
clearTimeout(timeout);
timeout = setTimeout(() => setJingle(false), 3000);
});
return () => cleanup();

return () => {
clearTimeout(timeout);
cleanup();
};
}, [novu]);

return (
<Popover onOpenChange={setOpen}>
<PopoverTrigger tabIndex={-1}>
<Bell
renderBell={(unreadCount) => (
<HeaderButton label={unreadCount ? `Inbox (${unreadCount})` : 'Inbox'} disableTooltip={open}>
<HeaderButton
label={
<>
Inbox
{isTestPage && ' (Test)'}
{unreadCount > 0 && ` (${unreadCount})`}
</>
}
disableTooltip={open}
className={isTestPage ? 'bg-test-pattern' : ''}
>
<div className="relative flex items-center justify-center">
<InboxBellFilled
className={`text-foreground-600 size-4 cursor-pointer stroke-[0.5px]`}
bellClassName={`origin-top ${jingle ? 'animate-swing' : ''}`}
ringerClassName={`origin-top ${jingle ? 'animate-jingle' : ''}`}
codeClassName={isTestPage ? 'block' : 'hidden'}
/>
{unreadCount > 0 && (
<div className="absolute right-[-4px] top-[-6px] flex h-3 w-3 items-center justify-center rounded-full border-[3px] border-[white] bg-white">
Expand All @@ -58,17 +79,36 @@ const InboxInner = () => {

export const InboxButton = () => {
const { user } = useUser();
const { currentEnvironment } = useEnvironment();
const { isTestPage } = useTestPage();

if (!user) {
if (!user || !currentEnvironment) {
return null;
}

/**
* If the page is a test page, we use the environment identifier as the appId.
*
* This displays a test inbox, where the user can see their test notifications appear
* in real-time.
*/
const appId = isTestPage ? currentEnvironment?.identifier : APP_ID;

const localizationTestSuffix = isTestPage ? ' (Test)' : '';

return (
<Inbox
subscriberId={user.externalId ?? ''}
applicationIdentifier={APP_ID}
applicationIdentifier={appId}
backendUrl={API_HOSTNAME}
socketUrl={WEBSOCKET_HOSTNAME}
localization={{
'inbox.filters.labels.default': `Inbox${localizationTestSuffix}`,
'inbox.filters.labels.unread': `Unread${localizationTestSuffix}`,
'inbox.filters.labels.archived': `Archived${localizationTestSuffix}`,
'preferences.title': `Preferences${localizationTestSuffix}`,
'notifications.emptyNotice': `${isTestPage ? 'This is a test inbox. Send a notification to preview it in real-time.' : 'No notifications'}`,
}}
>
<InboxInner />
</Inbox>
Expand Down
41 changes: 41 additions & 0 deletions apps/dashboard/src/components/primitives/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as React from 'react';

import { cn } from '@/utils/ui';

const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
<div ref={ref} className={cn('rounded-xl border border-neutral-200 bg-white shadow', className)} {...props} />
));
Card.displayName = 'Card';

const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
)
);
CardHeader.displayName = 'CardHeader';

const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn('font-semibold leading-none tracking-tight', className)} {...props} />
)
);
CardTitle.displayName = 'CardTitle';

const CardDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn('text-foreground-600 text-sm', className)} {...props} />
)
);
CardDescription.displayName = 'CardDescription';

const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
);
CardContent.displayName = 'CardContent';

const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
);
CardFooter.displayName = 'CardFooter';

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
9 changes: 9 additions & 0 deletions apps/dashboard/src/hooks/use-test-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useLocation } from 'react-router-dom';

export const useTestPage = () => {
const { pathname } = useLocation();

return {
isTestPage: pathname.includes('/test'),
};
};
18 changes: 3 additions & 15 deletions apps/dashboard/src/pages/organization-list.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
import { OrganizationList as OrganizationListForm } from '@clerk/clerk-react';
import { PageMeta } from '@/components/page-meta';
import { ROUTES } from '@/utils/routes';
import OrganizationCreate from '../components/auth/create-organization';

export const OrganizationListPage = () => {
return (
<>
<PageMeta title="Select or create organization" />
<OrganizationListForm
appearance={{
elements: {
organizationAvatarUploaderContainer: {
display: 'none',
},
},
}}
hidePersonal
skipInvitationScreen
afterSelectOrganizationUrl={ROUTES.ENV}
afterCreateOrganizationUrl={ROUTES.ENV}
/>

<OrganizationCreate />
</>
);
};
9 changes: 9 additions & 0 deletions apps/dashboard/src/pages/workflows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ import { RiSearch2Line } from 'react-icons/ri';
import { CreateWorkflowButton } from '@/components/create-workflow-button';
import { OptInModal } from '@/components/opt-in-modal';
import { PageMeta } from '@/components/page-meta';
import { useTelemetry } from '../hooks';
import { TelemetryEvent } from '../utils/telemetry';
import { useEffect } from 'react';

export const WorkflowsPage = () => {
const track = useTelemetry();

useEffect(() => {
track(TelemetryEvent.WORKFLOWS_PAGE_VISIT);
}, [track]);

return (
<>
<PageMeta title="Workflows" />
Expand Down
1 change: 1 addition & 0 deletions apps/dashboard/src/utils/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum TelemetryEvent {
SUBSCRIBERS_LINK_CLICKED = 'Subscribers link clicked - [Left navigation]',
NEW_DASHBOARD_OPT_OUT = 'New dashboard opt-out',
NEW_DASHBOARD_OPT_IN = 'New dashboard opt-in',
WORKFLOWS_PAGE_VISIT = 'Workflows page visit',
}
23 changes: 14 additions & 9 deletions apps/dashboard/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ export default {
md: '0px 16px 32px -12px rgba(14, 18, 27, 0.10)',
DEFAULT: '0px 16px 32px -12px #0E121B1A',
},

colors: {
black: 'black',
white: 'white',
transparent: 'transparent',
background: 'hsl(var(--background))',
foreground: {
Expand Down Expand Up @@ -153,15 +155,15 @@ export default {
},
swing: {
'0%, 9.9%, 100%': { transform: 'rotate(0deg)' },
'10%': { transform: 'rotate(2deg)' },
'20%': { transform: 'rotate(-2deg)' },
'30%': { transform: 'rotate(1.5deg)' },
'40%': { transform: 'rotate(-1.5deg)' },
'50%': { transform: 'rotate(1deg)' },
'60%': { transform: 'rotate(-1deg)' },
'70%': { transform: 'rotate(0.5deg)' },
'80%': { transform: 'rotate(-0.5deg)' },
'90%': { transform: 'rotate(0.2deg)' },
'10%': { transform: 'rotate(3deg)' },
'20%': { transform: 'rotate(-3deg)' },
'30%': { transform: 'rotate(2.25deg)' },
'40%': { transform: 'rotate(-2.25deg)' },
'50%': { transform: 'rotate(1.5deg)' },
'60%': { transform: 'rotate(-1.5deg)' },
'70%': { transform: 'rotate(0.75deg)' },
'80%': { transform: 'rotate(-0.75deg)' },
'90%': { transform: 'rotate(0.3deg)' },
},
jingle: {
'0%, 100%': { transform: 'rotate(0deg)' },
Expand All @@ -182,6 +184,9 @@ export default {
swing: 'swing 3s ease-in-out',
jingle: 'jingle 3s ease-in-out',
},
backgroundImage: {
'test-pattern': 'repeating-linear-gradient(135deg, hsl(var(--neutral-100)) 0, hsl(var(--neutral-100)) 2px, hsl(var(--neutral-200)) 2px, hsl(var(--neutral-200)) 4px)',
},
},
},
plugins: [require('tailwindcss-animate')],
Expand Down
Loading

0 comments on commit 7ae8769

Please sign in to comment.