fix: Consistency across buttons and labels
This commit is contained in:
@@ -42,32 +42,32 @@ export default function ActivityPage() {
|
||||
value: stats?.totalEvents?.toLocaleString() || '0',
|
||||
icon: Zap,
|
||||
description: 'Last 30 days',
|
||||
color: 'text-blue-600',
|
||||
bgColor: 'bg-blue-100',
|
||||
color: 'text-neutral-600',
|
||||
bgColor: 'bg-neutral-100',
|
||||
},
|
||||
{
|
||||
name: 'Emails Sent',
|
||||
value: stats?.totalEmailsSent?.toLocaleString() || '0',
|
||||
icon: Send,
|
||||
description: 'Last 30 days',
|
||||
color: 'text-green-600',
|
||||
bgColor: 'bg-green-100',
|
||||
color: 'text-neutral-600',
|
||||
bgColor: 'bg-neutral-100',
|
||||
},
|
||||
{
|
||||
name: 'Open Rate',
|
||||
value: stats?.openRate ? `${stats.openRate.toFixed(1)}%` : '0%',
|
||||
icon: Eye,
|
||||
description: `${stats?.totalEmailsOpened?.toLocaleString() || '0'} opens`,
|
||||
color: 'text-purple-600',
|
||||
bgColor: 'bg-purple-100',
|
||||
color: 'text-neutral-600',
|
||||
bgColor: 'bg-neutral-100',
|
||||
},
|
||||
{
|
||||
name: 'Click Rate',
|
||||
value: stats?.clickRate ? `${stats.clickRate.toFixed(1)}%` : '0%',
|
||||
icon: MousePointerClick,
|
||||
description: `${stats?.totalEmailsClicked?.toLocaleString() || '0'} clicks`,
|
||||
color: 'text-orange-600',
|
||||
bgColor: 'bg-orange-100',
|
||||
color: 'text-neutral-600',
|
||||
bgColor: 'bg-neutral-100',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@plunk/ui';
|
||||
import {EmptyState} from '@plunk/ui';
|
||||
import {DashboardLayout} from '../../components/DashboardLayout';
|
||||
import {EmptyState} from '../../components/EmptyState';
|
||||
import {useAnalytics} from '../../lib/hooks/useAnalytics';
|
||||
import useSWR from 'swr';
|
||||
import {
|
||||
@@ -149,8 +149,8 @@ export default function AnalyticsPage() {
|
||||
value: stats?.totalEmailsSent?.toLocaleString() || cumulativeTotals.emails.toLocaleString(),
|
||||
icon: Send,
|
||||
description: `Last ${days} days`,
|
||||
color: 'text-blue-600',
|
||||
bgColor: 'bg-blue-100',
|
||||
color: 'text-neutral-600',
|
||||
bgColor: 'bg-neutral-100',
|
||||
trend: stats?.totalEmailsSent ? (stats.totalEmailsSent > 0 ? 'positive' : 'neutral') : 'neutral',
|
||||
},
|
||||
{
|
||||
@@ -158,8 +158,8 @@ export default function AnalyticsPage() {
|
||||
value: stats?.openRate ? `${stats.openRate.toFixed(1)}%` : '0%',
|
||||
icon: Eye,
|
||||
description: `${stats?.totalEmailsOpened?.toLocaleString() || cumulativeTotals.opens.toLocaleString()} opens`,
|
||||
color: 'text-green-600',
|
||||
bgColor: 'bg-green-100',
|
||||
color: 'text-neutral-600',
|
||||
bgColor: 'bg-neutral-100',
|
||||
trend: stats?.openRate && stats.openRate > 20 ? 'positive' : 'neutral',
|
||||
},
|
||||
{
|
||||
@@ -167,8 +167,8 @@ export default function AnalyticsPage() {
|
||||
value: stats?.clickRate ? `${stats.clickRate.toFixed(1)}%` : '0%',
|
||||
icon: MousePointerClick,
|
||||
description: `${stats?.totalEmailsClicked?.toLocaleString() || cumulativeTotals.clicks.toLocaleString()} clicks`,
|
||||
color: 'text-purple-600',
|
||||
bgColor: 'bg-purple-100',
|
||||
color: 'text-neutral-600',
|
||||
bgColor: 'bg-neutral-100',
|
||||
trend: stats?.clickRate && stats.clickRate > 3 ? 'positive' : 'neutral',
|
||||
},
|
||||
{
|
||||
@@ -176,8 +176,8 @@ export default function AnalyticsPage() {
|
||||
value: campaignStats?.active?.toLocaleString() || '0',
|
||||
icon: Megaphone,
|
||||
description: `${campaignStats?.total || 0} total campaigns`,
|
||||
color: 'text-indigo-600',
|
||||
bgColor: 'bg-indigo-100',
|
||||
color: 'text-neutral-600',
|
||||
bgColor: 'bg-neutral-100',
|
||||
trend: campaignStats?.active ? 'positive' : 'neutral',
|
||||
},
|
||||
{
|
||||
@@ -185,8 +185,8 @@ export default function AnalyticsPage() {
|
||||
value: stats?.totalWorkflowsStarted?.toLocaleString() || '0',
|
||||
icon: Activity,
|
||||
description: 'Automations triggered',
|
||||
color: 'text-cyan-600',
|
||||
bgColor: 'bg-cyan-100',
|
||||
color: 'text-neutral-600',
|
||||
bgColor: 'bg-neutral-100',
|
||||
trend: stats?.totalWorkflowsStarted && stats.totalWorkflowsStarted > 0 ? 'positive' : 'neutral',
|
||||
},
|
||||
{
|
||||
@@ -194,8 +194,8 @@ export default function AnalyticsPage() {
|
||||
value: stats?.totalEvents?.toLocaleString() || '0',
|
||||
icon: Zap,
|
||||
description: 'Custom events tracked',
|
||||
color: 'text-orange-600',
|
||||
bgColor: 'bg-orange-100',
|
||||
color: 'text-neutral-600',
|
||||
bgColor: 'bg-neutral-100',
|
||||
trend: stats?.totalEvents && stats.totalEvents > 0 ? 'positive' : 'neutral',
|
||||
},
|
||||
];
|
||||
@@ -457,8 +457,8 @@ export default function AnalyticsPage() {
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="h-8 w-8 rounded-lg bg-green-100 flex items-center justify-center flex-shrink-0">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600" />
|
||||
<div className="h-8 w-8 rounded-lg bg-neutral-100 flex items-center justify-center flex-shrink-0">
|
||||
<CheckCircle2 className="h-4 w-4 text-neutral-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-neutral-900">Open Rate</p>
|
||||
@@ -470,8 +470,8 @@ export default function AnalyticsPage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="h-8 w-8 rounded-lg bg-blue-100 flex items-center justify-center flex-shrink-0">
|
||||
<BarChart3 className="h-4 w-4 text-blue-600" />
|
||||
<div className="h-8 w-8 rounded-lg bg-neutral-100 flex items-center justify-center flex-shrink-0">
|
||||
<BarChart3 className="h-4 w-4 text-neutral-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-neutral-900">Click Rate</p>
|
||||
@@ -483,8 +483,8 @@ export default function AnalyticsPage() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="h-8 w-8 rounded-lg bg-purple-100 flex items-center justify-center flex-shrink-0">
|
||||
<Zap className="h-4 w-4 text-purple-600" />
|
||||
<div className="h-8 w-8 rounded-lg bg-neutral-100 flex items-center justify-center flex-shrink-0">
|
||||
<Zap className="h-4 w-4 text-neutral-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-neutral-900">Engagement</p>
|
||||
@@ -509,8 +509,8 @@ export default function AnalyticsPage() {
|
||||
<p className="text-2xl font-bold text-neutral-900">{stats?.totalEvents?.toLocaleString() || '0'}</p>
|
||||
<p className="text-sm text-neutral-500">Total Events</p>
|
||||
</div>
|
||||
<div className="h-12 w-12 rounded-lg bg-orange-100 flex items-center justify-center">
|
||||
<Zap className="h-6 w-6 text-orange-600" />
|
||||
<div className="h-12 w-12 rounded-lg bg-neutral-100 flex items-center justify-center">
|
||||
<Zap className="h-6 w-6 text-neutral-600" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-4 border-t">
|
||||
@@ -566,11 +566,7 @@ export default function AnalyticsPage() {
|
||||
<td className="text-right py-3 px-4">
|
||||
<span
|
||||
className={`text-sm font-medium ${
|
||||
campaign.openRate > 30
|
||||
? 'text-green-600'
|
||||
: campaign.openRate > 20
|
||||
? 'text-blue-600'
|
||||
: 'text-neutral-600'
|
||||
campaign.openRate > 30 ? 'text-green-700' : 'text-neutral-700'
|
||||
}`}
|
||||
>
|
||||
{campaign.openRate.toFixed(1)}%
|
||||
@@ -579,11 +575,7 @@ export default function AnalyticsPage() {
|
||||
<td className="text-right py-3 px-4">
|
||||
<span
|
||||
className={`text-sm font-medium ${
|
||||
campaign.clickRate > 5
|
||||
? 'text-green-600'
|
||||
: campaign.clickRate > 3
|
||||
? 'text-blue-600'
|
||||
: 'text-neutral-600'
|
||||
campaign.clickRate > 5 ? 'text-green-700' : 'text-neutral-700'
|
||||
}`}
|
||||
>
|
||||
{campaign.clickRate.toFixed(1)}%
|
||||
|
||||
@@ -17,8 +17,8 @@ import {
|
||||
import type {Campaign, Template} from '@plunk/db';
|
||||
import {CampaignStatus} from '@plunk/db';
|
||||
import type {PaginatedResponse} from '@plunk/types';
|
||||
import {EmptyState} from '@plunk/ui';
|
||||
import {DashboardLayout} from '../../components/DashboardLayout';
|
||||
import {EmptyState} from '../../components/EmptyState';
|
||||
import {TemplateSelectionDialog} from '../../components/TemplateSelectionDialog';
|
||||
import {CampaignSelectionDialog} from '../../components/CampaignSelectionDialog';
|
||||
import {network} from '../../lib/network';
|
||||
@@ -49,15 +49,15 @@ export default function CampaignsPage() {
|
||||
);
|
||||
|
||||
const getStatusBadge = (status: CampaignStatus) => {
|
||||
const config: Record<CampaignStatus, {label: string; className: string}> = {
|
||||
DRAFT: {label: 'Draft', className: 'bg-neutral-100 text-neutral-600 border-transparent'},
|
||||
SCHEDULED: {label: 'Scheduled', className: 'bg-blue-100 text-blue-700 border-transparent'},
|
||||
SENDING: {label: 'Sending', className: 'bg-purple-100 text-purple-700 border-transparent'},
|
||||
SENT: {label: 'Sent', className: 'bg-green-100 text-green-700 border-transparent'},
|
||||
CANCELLED: {label: 'Cancelled', className: 'bg-red-100 text-red-700 border-transparent'},
|
||||
const config: Record<CampaignStatus, {label: string; variant: 'neutral' | 'default' | 'success'}> = {
|
||||
DRAFT: {label: 'Draft', variant: 'neutral'},
|
||||
SCHEDULED: {label: 'Scheduled', variant: 'default'},
|
||||
SENDING: {label: 'Sending', variant: 'default'},
|
||||
SENT: {label: 'Sent', variant: 'success'},
|
||||
CANCELLED: {label: 'Cancelled', variant: 'neutral'},
|
||||
};
|
||||
const {label, className} = config[status];
|
||||
return <Badge className={`${className} shrink-0`}>{label}</Badge>;
|
||||
const {label, variant} = config[status];
|
||||
return <Badge variant={variant} className="shrink-0">{label}</Badge>;
|
||||
};
|
||||
|
||||
const handleCancel = async () => {
|
||||
|
||||
@@ -19,8 +19,8 @@ import {
|
||||
} from '@plunk/ui';
|
||||
import type {Contact} from '@plunk/db';
|
||||
import type {CursorPaginatedResponse} from '@plunk/types';
|
||||
import {EmptyState} from '@plunk/ui';
|
||||
import {DashboardLayout} from '../../components/DashboardLayout';
|
||||
import {EmptyState} from '../../components/EmptyState';
|
||||
import {KeyValueEditor} from '../../components/KeyValueEditor';
|
||||
import {network} from '../../lib/network';
|
||||
import {formatRelativeTime} from '../../lib/dateUtils';
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Skeleton,
|
||||
} from '@plunk/ui';
|
||||
import {AlertCircle, Mail, Send, TrendingUp, Users} from 'lucide-react';
|
||||
import {NextSeo} from 'next-seo';
|
||||
@@ -192,11 +193,7 @@ export default function Index() {
|
||||
<Icon className="h-4 w-4 text-neutral-500" />
|
||||
</div>
|
||||
<CardTitle className="text-2xl tabular-nums">
|
||||
{isLoading ? (
|
||||
<div className="h-7 w-16 bg-neutral-100 rounded animate-pulse" />
|
||||
) : (
|
||||
stat.value
|
||||
)}
|
||||
{isLoading ? <Skeleton className="h-7 w-16" /> : stat.value}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Button} from '@plunk/ui';
|
||||
import {Button, Skeleton} from '@plunk/ui';
|
||||
import {motion} from 'framer-motion';
|
||||
import {ArrowLeft, ArrowRight, KeyRound, Mail, ShieldCheck} from 'lucide-react';
|
||||
import {NextSeo} from 'next-seo';
|
||||
@@ -192,7 +192,7 @@ export default function OnboardingDeveloper() {
|
||||
isSecret
|
||||
/>
|
||||
) : (
|
||||
<div className="h-16 animate-pulse rounded-lg bg-neutral-100" />
|
||||
<Skeleton className="h-16 rounded-lg" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Button} from '@plunk/ui';
|
||||
import {Button, Skeleton} from '@plunk/ui';
|
||||
import {motion} from 'framer-motion';
|
||||
import {ArrowLeft, ArrowRight, FileText, ShieldCheck, Workflow, Zap} from 'lucide-react';
|
||||
import {NextSeo} from 'next-seo';
|
||||
@@ -201,7 +201,7 @@ export default function OnboardingWorkflows() {
|
||||
description="Use this key to track events from browsers or clients."
|
||||
/>
|
||||
) : (
|
||||
<div className="h-16 animate-pulse rounded-lg bg-neutral-100" />
|
||||
<Skeleton className="h-16 rounded-lg" />
|
||||
)}
|
||||
<div className="overflow-hidden rounded-lg border border-neutral-200">
|
||||
<CodeTabs snippets={snippets} />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -219,14 +220,10 @@ export default function SegmentDetailPage() {
|
||||
</Link>
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h1 className="text-3xl font-bold text-neutral-900">{segment.name}</h1>
|
||||
<span
|
||||
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
|
||||
isStatic ? 'bg-purple-100 text-purple-700' : 'bg-blue-100 text-blue-700'
|
||||
}`}
|
||||
>
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-neutral-900">{segment.name}</h1>
|
||||
<Badge variant={isStatic ? 'neutral' : 'default'}>
|
||||
{isStatic ? 'Static' : 'Dynamic'}
|
||||
</span>
|
||||
</Badge>
|
||||
</div>
|
||||
{segment.description && <p className="text-neutral-500 mt-1">{segment.description}</p>}
|
||||
</div>
|
||||
@@ -395,11 +392,10 @@ export default function SegmentDetailPage() {
|
||||
</Link>
|
||||
{isStatic && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
variant="destructiveGhost"
|
||||
size="sm"
|
||||
onClick={() => handleRemoveMember(contact.email)}
|
||||
disabled={removingEmail === contact.email}
|
||||
className="text-red-600 hover:text-red-700 hover:bg-red-50"
|
||||
>
|
||||
<UserMinus className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
} from '@plunk/ui';
|
||||
import type {Segment} from '@plunk/db';
|
||||
import type {FilterCondition} from '@plunk/types';
|
||||
import {EmptyState} from '@plunk/ui';
|
||||
import {DashboardLayout} from '../../components/DashboardLayout';
|
||||
import {EmptyState} from '../../components/EmptyState';
|
||||
import {network} from '../../lib/network';
|
||||
import {formatRelativeTime} from '../../lib/dateUtils';
|
||||
import {AlertTriangle, Calendar, Edit, Filter, Plus, Search, Trash2, X} from 'lucide-react';
|
||||
@@ -170,7 +170,7 @@ export default function SegmentsPage() {
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3 mb-3">
|
||||
<h3 className="font-semibold text-neutral-900 leading-snug">{segment.name}</h3>
|
||||
<Badge variant={isDynamic ? 'info' : 'default'} className="shrink-0 mt-0.5">
|
||||
<Badge variant={isDynamic ? 'default' : 'neutral'} className="shrink-0 mt-0.5">
|
||||
{isDynamic ? 'Dynamic' : 'Static'}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
@@ -85,7 +85,7 @@ export default function NewSegmentPage() {
|
||||
</Button>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-neutral-900">Create Segment</h1>
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-neutral-900">Create Segment</h1>
|
||||
<p className="text-neutral-500 mt-1">
|
||||
{segmentType === 'DYNAMIC'
|
||||
? 'Build complex audience filters with AND/OR logic'
|
||||
|
||||
@@ -375,7 +375,7 @@ export default function Settings() {
|
||||
<div className="space-y-8">
|
||||
{/* Header */}
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-neutral-900">Settings</h1>
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-neutral-900">Settings</h1>
|
||||
<p className="text-neutral-500 mt-2">Manage your project settings and preferences</p>
|
||||
</div>
|
||||
|
||||
@@ -574,7 +574,7 @@ export default function Settings() {
|
||||
<div className="flex items-start justify-between gap-4 p-5 rounded-lg border border-neutral-200 bg-white transition-all">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Database className="h-4 w-4 text-orange-600" />
|
||||
<Database className="h-4 w-4 text-amber-700" />
|
||||
<h4 className="font-semibold text-neutral-900">Reset Project Data</h4>
|
||||
</div>
|
||||
<p className="text-sm text-neutral-600 mb-3">
|
||||
@@ -586,12 +586,7 @@ export default function Settings() {
|
||||
<span>API keys, domains, billing information</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => setShowResetDialog(true)}
|
||||
className="shrink-0 border-orange-300 text-orange-700 hover:bg-orange-50 hover:border-orange-400"
|
||||
>
|
||||
<Button type="button" variant="outline" onClick={() => setShowResetDialog(true)} className="shrink-0">
|
||||
Reset Data
|
||||
</Button>
|
||||
</div>
|
||||
@@ -823,7 +818,7 @@ export default function Settings() {
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<AlertTriangle className="h-5 w-5 text-orange-500" />
|
||||
<AlertTriangle className="h-5 w-5 text-amber-600" />
|
||||
Regenerate API Keys
|
||||
</DialogTitle>
|
||||
<DialogDescription className="space-y-3">
|
||||
@@ -841,7 +836,7 @@ export default function Settings() {
|
||||
<Button variant="outline" onClick={() => setShowRegenerateDialog(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="default" onClick={handleRegenerateKeys} className="bg-orange-600 hover:bg-orange-700">
|
||||
<Button variant="destructive" onClick={handleRegenerateKeys}>
|
||||
Regenerate Keys
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
@@ -852,8 +847,8 @@ export default function Settings() {
|
||||
<Dialog open={showResetDialog} onOpenChange={setShowResetDialog}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader className="space-y-3">
|
||||
<div className="mx-auto w-12 h-12 bg-orange-100 rounded-full flex items-center justify-center">
|
||||
<Database className="h-6 w-6 text-orange-600" />
|
||||
<div className="mx-auto w-12 h-12 bg-amber-100 rounded-full flex items-center justify-center">
|
||||
<Database className="h-6 w-6 text-amber-700" />
|
||||
</div>
|
||||
<DialogTitle className="text-center text-xl">Reset Project Data?</DialogTitle>
|
||||
<DialogDescription className="text-center text-base">
|
||||
@@ -865,7 +860,7 @@ export default function Settings() {
|
||||
<div className="py-4">
|
||||
<label className="text-sm font-medium text-neutral-700 block mb-2 text-center">
|
||||
Type{' '}
|
||||
<span className="font-mono font-semibold text-orange-600 bg-orange-50 px-2 py-0.5 rounded">RESET</span>{' '}
|
||||
<span className="font-mono font-semibold text-amber-700 bg-amber-50 px-2 py-0.5 rounded">RESET</span>{' '}
|
||||
to confirm
|
||||
</label>
|
||||
<Input
|
||||
@@ -889,9 +884,10 @@ export default function Settings() {
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleResetProject}
|
||||
disabled={resetConfirmText !== 'RESET'}
|
||||
className="w-full bg-orange-600 hover:bg-orange-700 disabled:opacity-50"
|
||||
className="w-full"
|
||||
>
|
||||
Reset Data
|
||||
</Button>
|
||||
@@ -946,9 +942,10 @@ export default function Settings() {
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleDeleteProject}
|
||||
disabled={deleteConfirmText !== 'DELETE'}
|
||||
className="w-full bg-red-600 hover:bg-red-700 disabled:opacity-50"
|
||||
className="w-full"
|
||||
>
|
||||
Delete Forever
|
||||
</Button>
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
} from '@plunk/ui';
|
||||
import type {Template} from '@plunk/db';
|
||||
import type {PaginatedResponse} from '@plunk/types';
|
||||
import {EmptyState} from '@plunk/ui';
|
||||
import {DashboardLayout} from '../../components/DashboardLayout';
|
||||
import {EmptyState} from '../../components/EmptyState';
|
||||
import {network} from '../../lib/network';
|
||||
import {formatRelativeTime} from '../../lib/dateUtils';
|
||||
import {Calendar, Copy, Edit, FileText, Plus, Search, Trash2, X} from 'lucide-react';
|
||||
@@ -174,7 +174,7 @@ export default function TemplatesPage() {
|
||||
<h3 className="font-semibold text-neutral-900 leading-snug">{template.name}</h3>
|
||||
<Badge
|
||||
className="capitalize shrink-0 mt-0.5"
|
||||
variant={template.type === 'MARKETING' ? 'info' : template.type === 'HEADLESS' ? 'warning' : 'success'}
|
||||
variant="neutral"
|
||||
>
|
||||
{template.type.toLowerCase()}
|
||||
</Badge>
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
AlertTitle,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
EmptyState,
|
||||
Input,
|
||||
Label,
|
||||
Select,
|
||||
@@ -36,7 +38,6 @@ import {
|
||||
import type {Template, Workflow, WorkflowExecution, WorkflowStep, WorkflowTransition} from '@plunk/db';
|
||||
import type {PaginatedResponse} from '@plunk/types';
|
||||
import {DashboardLayout} from '../../components/DashboardLayout';
|
||||
import {EmptyState} from '../../components/EmptyState';
|
||||
import {network} from '../../lib/network';
|
||||
import {
|
||||
AlertTriangle,
|
||||
@@ -659,23 +660,21 @@ export default function WorkflowEditorPage() {
|
||||
{execution.contact.email}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<span
|
||||
className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
|
||||
<Badge
|
||||
variant={
|
||||
execution.status === 'COMPLETED'
|
||||
? 'bg-green-100 text-green-800'
|
||||
: execution.status === 'RUNNING'
|
||||
? 'bg-blue-100 text-blue-800'
|
||||
? 'success'
|
||||
: execution.status === 'FAILED'
|
||||
? 'destructive'
|
||||
: execution.status === 'WAITING'
|
||||
? 'bg-yellow-100 text-yellow-800'
|
||||
: execution.status === 'FAILED'
|
||||
? 'bg-red-100 text-red-800'
|
||||
: execution.status === 'CANCELLED'
|
||||
? 'bg-neutral-100 text-neutral-800'
|
||||
: 'bg-neutral-100 text-neutral-800'
|
||||
}`}
|
||||
? 'warning'
|
||||
: execution.status === 'RUNNING'
|
||||
? 'default'
|
||||
: 'neutral'
|
||||
}
|
||||
>
|
||||
{execution.status}
|
||||
</span>
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-neutral-500">
|
||||
{execution.currentStep?.name ?? '-'}
|
||||
@@ -690,13 +689,14 @@ export default function WorkflowEditorPage() {
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
{(execution.status === 'RUNNING' || execution.status === 'WAITING') && (
|
||||
<button
|
||||
<Button
|
||||
variant="destructiveGhost"
|
||||
size="sm"
|
||||
onClick={() => setExecutionToCancel(execution.id)}
|
||||
className="text-red-600 hover:text-red-900 disabled:opacity-50"
|
||||
disabled={isCancelling}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -2471,13 +2471,15 @@ function EditStepDialog({step, workflowId, open, onOpenChange, onSuccess}: EditS
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-xs font-semibold text-neutral-500">Branch {idx + 1}</span>
|
||||
{conditionBranches.length > 1 && (
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructiveGhost"
|
||||
size="icon"
|
||||
className="h-7 w-7"
|
||||
onClick={() => setConditionBranches(prev => prev.filter(b => b.id !== branch.id))}
|
||||
className="text-xs text-red-500 hover:text-red-700"
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2551,7 +2553,7 @@ function EditStepDialog({step, workflowId, open, onOpenChange, onSuccess}: EditS
|
||||
{id: crypto.randomUUID().slice(0, 8), name: '', operator: 'equals', value: ''},
|
||||
])
|
||||
}
|
||||
className="flex items-center gap-1.5 text-sm text-purple-600 hover:text-purple-800 font-medium"
|
||||
className="flex items-center gap-1.5 text-sm text-neutral-700 hover:text-neutral-900 font-medium"
|
||||
>
|
||||
<Plus className="h-3.5 w-3.5" />
|
||||
Add Branch
|
||||
|
||||
@@ -19,8 +19,8 @@ import {
|
||||
} from '@plunk/ui';
|
||||
import type {Workflow} from '@plunk/db';
|
||||
import type {PaginatedResponse} from '@plunk/types';
|
||||
import {EmptyState} from '@plunk/ui';
|
||||
import {DashboardLayout} from '../../components/DashboardLayout';
|
||||
import {EmptyState} from '../../components/EmptyState';
|
||||
import {network} from '../../lib/network';
|
||||
import {formatRelativeTime} from '../../lib/dateUtils';
|
||||
import {Calendar, Edit, Plus, Power, PowerOff, Search, Trash2, Workflow as WorkflowIcon, X, Zap} from 'lucide-react';
|
||||
@@ -165,7 +165,7 @@ export default function WorkflowsPage() {
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3 mb-3">
|
||||
<h3 className="font-semibold text-neutral-900 leading-snug">{workflow.name}</h3>
|
||||
<Badge variant={workflow.enabled ? 'green' : 'neutral'} className="shrink-0 mt-0.5">
|
||||
<Badge variant={workflow.enabled ? 'success' : 'neutral'} className="shrink-0 mt-0.5">
|
||||
{workflow.enabled ? (
|
||||
<><Power className="h-3 w-3 mr-1" />Active</>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user