Skip to content

Commit

Permalink
feat(dashboard): badge component (#7447)
Browse files Browse the repository at this point in the history
  • Loading branch information
scopsy authored Jan 12, 2025
1 parent 80c9ef9 commit 4ae7fe6
Show file tree
Hide file tree
Showing 20 changed files with 708 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function ActivityJobItem({ job, isFirst, isLast }: ActivityJobItemProps)
<CardContent className="rounded-lg bg-neutral-50 p-2">
<div className="flex items-center justify-between">
<span className="text-foreground-400 max-w-[300px] truncate pr-2 text-xs">{getStatusMessage(job)}</span>
<Badge variant="soft" className="bg-foreground-50 shrink-0 px-2 py-0.5 text-[11px] leading-3">
<Badge variant="lighter" color="gray" size="sm">
<TimeDisplayHoverCard date={new Date(job.updatedAt)}>
{format(new Date(job.updatedAt), 'MMM d yyyy, HH:mm:ss')}
</TimeDisplayHoverCard>
Expand Down
22 changes: 11 additions & 11 deletions apps/dashboard/src/components/activity/activity-table.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { ActivityFilters } from '@/api/activity';
import { Skeleton } from '@/components/primitives/skeleton';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/primitives/table';
import { format } from 'date-fns';
import { TimeDisplayHoverCard } from '@/components/time-display-hover-card';
import { cn } from '@/utils/ui';
import { ISubscriber } from '@novu/shared';
import { TimeDisplayHoverCard } from '@/components/time-display-hover-card';
import { createSearchParams, useLocation, useSearchParams, useNavigate } from 'react-router-dom';
import { StatusBadge } from './components/status-badge';
import { StepIndicators } from './components/step-indicators';
import { ActivityEmptyState } from './activity-empty-state';
import { format } from 'date-fns';
import { AnimatePresence, motion } from 'motion/react';
import { ArrowPagination } from './components/arrow-pagination';
import { useEffect } from 'react';
import { ActivityFilters } from '@/api/activity';
import { useFetchActivities } from '../../hooks/use-fetch-activities';
import { createSearchParams, useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import { toast } from 'sonner';
import { Skeleton } from '@/components/primitives/skeleton';
import { useFetchActivities } from '../../hooks/use-fetch-activities';
import { ActivityEmptyState } from './activity-empty-state';
import { ArrowPagination } from './components/arrow-pagination';
import { ActivityStatusBadge } from './components/status-badge';
import { StepIndicators } from './components/step-indicators';

export interface ActivityTableProps {
selectedActivityId: string | null;
Expand Down Expand Up @@ -120,7 +120,7 @@ export function ActivityTable({
</div>
</TableCell>
<TableCell className="px-3">
<StatusBadge jobs={activity.jobs} />
<ActivityStatusBadge jobs={activity.jobs} />
</TableCell>
<TableCell className="px-3">
<StepIndicators jobs={activity.jobs} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { useState, useRef, useEffect } from 'react';
import { Badge, BadgeVariant } from '@/components/primitives/badge';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/primitives/popover';
import { IActivityJob, JobStatusEnum } from '@novu/shared';
import { StatusPreviewCard } from './status-preview-card';
import { useEffect, useRef, useState } from 'react';
import { StatusBadge as StatusBadgeComponent, StatusBadgeIcon } from '../../primitives/status-badge';
import { JOB_STATUS_CONFIG } from '../constants';

import { StatusPreviewCard } from './status-preview-card';
export interface StatusBadgeProps {
jobs: IActivityJob[];
}

export function StatusBadge({ jobs }: StatusBadgeProps) {
export function ActivityStatusBadge({ jobs }: StatusBadgeProps) {
const [isOpen, setIsOpen] = useState(false);
const errorCount = jobs.filter((job) => job.status === JobStatusEnum.FAILED).length;
const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
Expand Down Expand Up @@ -49,10 +48,9 @@ export function StatusBadge({ jobs }: StatusBadgeProps) {
return (
<Popover open={isOpen}>
<PopoverTrigger onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
<Badge variant={variant as BadgeVariant} className="cursor-pointer gap-1 px-1 py-0 leading-6">
<Icon className="h-3.5 w-3.5" />
{displayLabel}
</Badge>
<StatusBadgeComponent variant="light" status={variant}>
<StatusBadgeIcon as={Icon} /> {displayLabel}
</StatusBadgeComponent>
</PopoverTrigger>
<PopoverContent
className="w-64"
Expand Down
25 changes: 12 additions & 13 deletions apps/dashboard/src/components/activity/constants.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { JobStatusEnum } from '@novu/shared';
import { RiCheckboxCircleFill, RiErrorWarningFill, RiForbidFill } from 'react-icons/ri';
import { BadgeVariant } from '../primitives/badge';
import { RiLoader3Line, RiLoader4Fill } from 'react-icons/ri';
import { IconType } from 'react-icons/lib';
import { RiCheckboxCircleFill, RiErrorWarningFill, RiForbidFill, RiLoader3Line, RiLoader4Fill } from 'react-icons/ri';
import { StatusBadgeProps } from '../primitives/status-badge';

export const STATUS_STYLES = {
completed: 'border-[#99e3bb] bg-[#e9faf0] text-[#99e3bb]',
Expand All @@ -14,64 +13,64 @@ export const STATUS_STYLES = {
export const JOB_STATUS_CONFIG: Record<
JobStatusEnum,
{
variant: BadgeVariant;
variant: StatusBadgeProps['status'];
color: string;
icon: IconType;
label: string;
animationClass?: string;
}
> = {
[JobStatusEnum.COMPLETED]: {
variant: 'success' as const,
variant: 'completed' as const,
color: 'success',
icon: RiCheckboxCircleFill,
label: 'SUCCESS',
},
[JobStatusEnum.FAILED]: {
variant: 'destructive' as const,
variant: 'failed' as const,
color: 'destructive',
icon: RiErrorWarningFill,
label: `ERROR`,
},
[JobStatusEnum.MERGED]: {
variant: 'success' as const,
variant: 'disabled' as const,
color: 'success',
icon: RiForbidFill,
label: 'MERGED',
},
[JobStatusEnum.PENDING]: {
variant: 'warning' as const,
variant: 'pending' as const,
icon: RiLoader3Line,
color: 'neutral-300',
label: 'PENDING',
},
[JobStatusEnum.CANCELED]: {
variant: 'warning' as const,
variant: 'disabled' as const,
icon: RiLoader3Line,
color: 'neutral-300',
label: 'CANCELED',
},
[JobStatusEnum.SKIPPED]: {
variant: 'warning' as const,
variant: 'disabled' as const,
icon: RiLoader3Line,
color: 'neutral-300',
label: 'SKIPPED',
},
[JobStatusEnum.RUNNING]: {
variant: 'warning' as const,
variant: 'pending' as const,
icon: RiLoader3Line,
color: 'warning',
label: 'RUNNING',
},
[JobStatusEnum.DELAYED]: {
variant: 'warning' as const,
variant: 'pending' as const,
icon: RiLoader4Fill,
label: 'DELAYED',
color: 'warning',
animationClass: 'animate-spin-slow',
},
[JobStatusEnum.QUEUED]: {
variant: 'warning' as const,
variant: 'pending' as const,
icon: RiLoader3Line,
color: 'warning',
label: 'QUEUED',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export function ActivePlanBanner({ selectedBillingInterval }: ActivePlanBannerPr
<h3 className="text-lg font-semibold capitalize">{subscription.apiServiceLevel?.toLowerCase()}</h3>
)}
{subscription?.trial.isActive && (
<Badge variant="outline" className="font-medium">
<Badge variant="light" color="gray" size="sm">
Trial
</Badge>
)}
Expand Down
7 changes: 6 additions & 1 deletion apps/dashboard/src/components/billing/highlights-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ function PlanHighlights({ planHighlights }: { planHighlights: Highlight[] }) {
{planHighlights.map((item, index) => (
<li key={index} className="flex items-center gap-2">
<div className="bg-primary h-1.5 w-1.5 rounded-full" />
{item.text} {item.badgeLabel && <Badge variant="outline">{item.badgeLabel}</Badge>}
{item.text}{' '}
{item.badgeLabel && (
<Badge variant="stroke" color="gray">
{item.badgeLabel}
</Badge>
)}
</li>
))}
</ul>
Expand Down
18 changes: 15 additions & 3 deletions apps/dashboard/src/components/billing/plans-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ export function PlansRow({ selectedBillingInterval, currentPlan, trial }: PlansR
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-xl font-semibold">Free</h3>
{effectiveCurrentPlan === 'free' && <Badge variant="soft">Current Plan</Badge>}
{effectiveCurrentPlan === 'free' && (
<Badge variant="light" color="gray" size="sm">
Current Plan
</Badge>
)}
</div>
<PlanDisplay price="$0" subtitle="free forever" events="30,000 events per month" />
<ul className="space-y-2">
Expand Down Expand Up @@ -73,7 +77,11 @@ export function PlansRow({ selectedBillingInterval, currentPlan, trial }: PlansR
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-xl font-semibold">Business</h3>
{effectiveCurrentPlan === 'business' && <Badge variant="soft">Current Plan</Badge>}
{effectiveCurrentPlan === 'business' && (
<Badge variant="light" color="gray" size="sm">
Current Plan
</Badge>
)}
</div>
<PlanDisplay
price={businessPlanPrice}
Expand Down Expand Up @@ -111,7 +119,11 @@ export function PlansRow({ selectedBillingInterval, currentPlan, trial }: PlansR
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-xl font-semibold">Enterprise</h3>
{effectiveCurrentPlan === 'enterprise' && <Badge variant="soft">Current Plan</Badge>}
{effectiveCurrentPlan === 'enterprise' && (
<Badge variant="light" color="gray" size="sm">
Current Plan
</Badge>
)}
</div>
<div className="space-y-1">
<div className="flex items-baseline gap-1">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@ import { Button } from '@/components/primitives/button';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/primitives/tooltip';
import { ROUTES } from '@/utils/routes';
import { ChannelTypeEnum, type IEnvironment, type IIntegration, type IProviderConfig } from '@novu/shared';
import { RiCheckboxCircleFill, RiGitBranchFill, RiSettings4Line, RiStarSmileLine } from 'react-icons/ri';
import {
RiCheckboxCircleFill,
RiCloseCircleFill,
RiGitBranchFill,
RiSettings4Line,
RiStarSmileLine,
} from 'react-icons/ri';
import { useNavigate } from 'react-router-dom';
import { cn } from '../../../utils/ui';
import { StatusBadge, StatusBadgeIcon } from '../../primitives/status-badge';
import { TableIntegration } from '../types';
import { ProviderIcon } from './provider-icon';
import { isDemoIntegration } from './utils/helpers';
Expand Down Expand Up @@ -74,7 +81,7 @@ export function IntegrationCard({ integration, provider, environment, onClick }:
<Tooltip>
<TooltipTrigger className="flex h-[16px] items-center gap-1">
<span className="flex h-[16px] items-center gap-1">
<Badge variant="warning" className="text-2xs h-[16px] rounded-sm text-[#F6B51E]">
<Badge variant="lighter" color="yellow" size="sm">
DEMO
</Badge>
</span>
Expand Down Expand Up @@ -102,17 +109,17 @@ export function IntegrationCard({ integration, provider, environment, onClick }:
Connect
</Button>
) : (
<Badge variant={integration.active ? 'success' : 'neutral'} className="capitalize">
<RiCheckboxCircleFill className="text-success h-4 w-4" />
<StatusBadge variant="light" status={integration.active ? 'completed' : 'disabled'}>
<StatusBadgeIcon as={integration.active ? RiCheckboxCircleFill : RiCloseCircleFill} />
{integration.active ? 'Active' : 'Inactive'}
</Badge>
</StatusBadge>
)}
<Badge variant="outline" className="shadow-none">
<StatusBadge variant="stroke" status="pending" className="gap-1 shadow-none">
<RiGitBranchFill
className={cn('h-4 w-4', environment.name.toLowerCase() === 'production' ? 'text-feature' : 'text-warning')}
/>
{environment.name}
</Badge>
</StatusBadge>
</div>
</div>
);
Expand Down
Loading

0 comments on commit 4ae7fe6

Please sign in to comment.