Selectors with description
This commit is contained in:
@@ -29,6 +29,7 @@ import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectItemWithDescription,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Table,
|
||||
@@ -346,8 +347,16 @@ export function TeamSettings({projectId, currentUserRole, currentUserId}: TeamSe
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="MEMBER">Member - Can view and use the project</SelectItem>
|
||||
<SelectItem value="ADMIN">Admin - Can manage settings and members</SelectItem>
|
||||
<SelectItemWithDescription
|
||||
value="MEMBER"
|
||||
title="Member"
|
||||
description="Can view and use the project"
|
||||
/>
|
||||
<SelectItemWithDescription
|
||||
value="ADMIN"
|
||||
title="Admin"
|
||||
description="Can manage settings and members"
|
||||
/>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
|
||||
@@ -2,6 +2,7 @@ import {useEffect, useState} from 'react';
|
||||
import {useForm} from 'react-hook-form';
|
||||
import {zodResolver} from '@hookform/resolvers/zod';
|
||||
import {ProjectSchemas} from '@plunk/shared';
|
||||
import {TrackingMode} from '@plunk/db';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
@@ -24,7 +25,11 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
Input,
|
||||
Switch,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItemWithDescription,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
@@ -158,7 +163,7 @@ export default function Settings() {
|
||||
resolver: zodResolver(ProjectSchemas.update),
|
||||
defaultValues: {
|
||||
name: activeProject?.name || '',
|
||||
trackingEnabled: activeProject?.trackingEnabled ?? true,
|
||||
tracking: activeProject?.tracking ?? TrackingMode.ENABLED,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -167,7 +172,7 @@ export default function Settings() {
|
||||
if (activeProject) {
|
||||
form.reset({
|
||||
name: activeProject.name,
|
||||
trackingEnabled: activeProject.trackingEnabled ?? true,
|
||||
tracking: activeProject.tracking ?? TrackingMode.ENABLED,
|
||||
});
|
||||
}
|
||||
}, [activeProject, form]);
|
||||
@@ -395,23 +400,42 @@ export default function Settings() {
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Email Tracking Toggle - only show if feature is available */}
|
||||
{/* Email Tracking Mode - only show if feature is available */}
|
||||
{trackingToggleEnabled && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="trackingEnabled"
|
||||
name="tracking"
|
||||
render={({field}) => (
|
||||
<FormItem className="flex flex-row items-center justify-between rounded-lg border border-neutral-200 p-4">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel className="text-base">Email Tracking</FormLabel>
|
||||
<FormDescription>
|
||||
Enable open and click tracking for emails sent from this project. When disabled,
|
||||
emails will be sent without tracking pixels.
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
<FormItem>
|
||||
<FormLabel>Email Tracking</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select tracking mode" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItemWithDescription
|
||||
value={TrackingMode.ENABLED}
|
||||
title="Enabled"
|
||||
description="Track opens and clicks for all emails"
|
||||
/>
|
||||
<SelectItemWithDescription
|
||||
value={TrackingMode.DISABLED}
|
||||
title="Disabled"
|
||||
description="No tracking for any emails"
|
||||
/>
|
||||
<SelectItemWithDescription
|
||||
value={TrackingMode.MARKETING_ONLY}
|
||||
title="Marketing Only"
|
||||
description="Track only campaigns and workflow emails, not transactional"
|
||||
/>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormDescription>
|
||||
Control how email opens and clicks are tracked for this project.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
@@ -659,7 +683,11 @@ export default function Settings() {
|
||||
|
||||
{/* Team Tab */}
|
||||
<TabsContent value="team">
|
||||
<TeamSettings projectId={activeProject.id} currentUserRole={currentUserRole} currentUserId={user?.id || ''} />
|
||||
<TeamSettings
|
||||
projectId={activeProject.id}
|
||||
currentUserRole={currentUserRole}
|
||||
currentUserId={user?.id || ''}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
{/* Domains Tab */}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectItemWithDescription,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
StickySaveBar,
|
||||
@@ -232,8 +232,16 @@ export default function TemplateEditorPage() {
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="MARKETING">Marketing</SelectItem>
|
||||
<SelectItem value="TRANSACTIONAL">Transactional</SelectItem>
|
||||
<SelectItemWithDescription
|
||||
value="MARKETING"
|
||||
title="Marketing"
|
||||
description="Includes unsubscribe link, respects opt-out"
|
||||
/>
|
||||
<SelectItemWithDescription
|
||||
value="TRANSACTIONAL"
|
||||
title="Transactional"
|
||||
description="For receipts, alerts - sent regardless of opt-out"
|
||||
/>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectItemWithDescription,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@plunk/ui';
|
||||
@@ -74,117 +74,127 @@ export default function CreateTemplatePage() {
|
||||
return (
|
||||
<>
|
||||
<NextSeo title="Create Template" />
|
||||
<DashboardLayout>
|
||||
<div className="max-w-5xl mx-auto space-y-6">
|
||||
{/* Header */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3 sm:gap-4">
|
||||
<Link href="/templates">
|
||||
<Button variant="ghost" size="sm">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
<DashboardLayout>
|
||||
<div className="max-w-5xl mx-auto space-y-6">
|
||||
{/* Header */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3 sm:gap-4">
|
||||
<Link href="/templates">
|
||||
<Button variant="ghost" size="sm">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-neutral-900">Create Template</h1>
|
||||
<p className="text-neutral-500 mt-1 text-sm sm:text-base">
|
||||
Create a reusable email template for campaigns and workflows
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={handleSubmit} disabled={saving} className="w-full sm:w-auto">
|
||||
<Save className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">{saving ? 'Creating...' : 'Create Template'}</span>
|
||||
<span className="sm:hidden">{saving ? 'Creating...' : 'Create'}</span>
|
||||
</Button>
|
||||
</Link>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h1 className="text-2xl sm:text-3xl font-bold text-neutral-900">Create Template</h1>
|
||||
<p className="text-neutral-500 mt-1 text-sm sm:text-base">Create a reusable email template for campaigns and workflows</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={handleSubmit} disabled={saving} className="w-full sm:w-auto">
|
||||
<Save className="h-4 w-4" />
|
||||
<span className="hidden sm:inline">{saving ? 'Creating...' : 'Create Template'}</span>
|
||||
<span className="sm:hidden">{saving ? 'Creating...' : 'Create'}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* Template Settings */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Template Settings</CardTitle>
|
||||
<CardDescription>Configure your template details and email settings</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* Template Settings */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Template Settings</CardTitle>
|
||||
<CardDescription>Configure your template details and email settings</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="name">Template Name *</Label>
|
||||
<Input
|
||||
id="name"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
required
|
||||
placeholder="Welcome Email"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="type">Template Type *</Label>
|
||||
<Select value={type} onValueChange={value => setType(value as 'MARKETING' | 'TRANSACTIONAL')}>
|
||||
<SelectTrigger id="type">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItemWithDescription
|
||||
value="MARKETING"
|
||||
title="Marketing"
|
||||
description="Includes unsubscribe link, respects opt-out"
|
||||
/>
|
||||
<SelectItemWithDescription
|
||||
value="TRANSACTIONAL"
|
||||
title="Transactional"
|
||||
description="For receipts, alerts - sent regardless of opt-out"
|
||||
/>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="name">Template Name *</Label>
|
||||
<Label htmlFor="description">Description</Label>
|
||||
<Input
|
||||
id="name"
|
||||
id="description"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
required
|
||||
placeholder="Welcome Email"
|
||||
value={description}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
placeholder="Sent to new subscribers"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="type">Template Type *</Label>
|
||||
<Select value={type} onValueChange={value => setType(value as 'MARKETING' | 'TRANSACTIONAL')}>
|
||||
<SelectTrigger id="type">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="MARKETING">Marketing</SelectItem>
|
||||
<SelectItem value="TRANSACTIONAL">Transactional</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label htmlFor="subject">Subject Line *</Label>
|
||||
<Input
|
||||
id="subject"
|
||||
type="text"
|
||||
value={subject}
|
||||
onChange={e => setSubject(e.target.value)}
|
||||
required
|
||||
placeholder="Welcome to our platform!"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="description">Description</Label>
|
||||
<Input
|
||||
id="description"
|
||||
type="text"
|
||||
value={description}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
placeholder="Sent to new subscribers"
|
||||
<EmailSettings
|
||||
from={from}
|
||||
fromName={fromName}
|
||||
replyTo={replyTo}
|
||||
onFromChange={setFrom}
|
||||
onFromNameChange={setFromName}
|
||||
onReplyToChange={setReplyTo}
|
||||
fromNamePlaceholder={activeProject?.name || 'Your Company'}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="subject">Subject Line *</Label>
|
||||
<Input
|
||||
id="subject"
|
||||
type="text"
|
||||
value={subject}
|
||||
onChange={e => setSubject(e.target.value)}
|
||||
required
|
||||
placeholder="Welcome to our platform!"
|
||||
{/* Email Body */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Email Body</CardTitle>
|
||||
<CardDescription>Create your email using the visual editor or paste custom HTML</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<EmailEditor
|
||||
value={body}
|
||||
onChange={setBody}
|
||||
placeholder="<h1>Welcome!</h1><p>Thanks for subscribing to our newsletter.</p>"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<EmailSettings
|
||||
from={from}
|
||||
fromName={fromName}
|
||||
replyTo={replyTo}
|
||||
onFromChange={setFrom}
|
||||
onFromNameChange={setFromName}
|
||||
onReplyToChange={setReplyTo}
|
||||
fromNamePlaceholder={activeProject?.name || 'Your Company'}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Email Body */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Email Body</CardTitle>
|
||||
<CardDescription>Create your email using the visual editor or paste custom HTML</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<EmailEditor
|
||||
value={body}
|
||||
onChange={setBody}
|
||||
placeholder="<h1>Welcome!</h1><p>Thanks for subscribing to our newsletter.</p>"
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</form>
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</form>
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectItemWithDescription,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Switch
|
||||
@@ -1076,13 +1077,37 @@ function AddStepDialog({open, onOpenChange, workflowId, onSuccess}: AddStepDialo
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="SEND_EMAIL">Send Email</SelectItem>
|
||||
<SelectItem value="DELAY">Delay - Wait for time</SelectItem>
|
||||
<SelectItem value="WAIT_FOR_EVENT">Wait for Event</SelectItem>
|
||||
<SelectItem value="CONDITION">Condition - If/else branching</SelectItem>
|
||||
<SelectItem value="WEBHOOK">Webhook - Call external API</SelectItem>
|
||||
<SelectItem value="UPDATE_CONTACT">Update Contact</SelectItem>
|
||||
<SelectItem value="EXIT">Exit - End workflow</SelectItem>
|
||||
<SelectItemWithDescription
|
||||
value="SEND_EMAIL"
|
||||
title="Send Email"
|
||||
description="Send an email using a template"
|
||||
/>
|
||||
<SelectItemWithDescription
|
||||
value="DELAY"
|
||||
title="Delay"
|
||||
description="Wait for a specified amount of time"
|
||||
/>
|
||||
<SelectItemWithDescription
|
||||
value="WAIT_FOR_EVENT"
|
||||
title="Wait for Event"
|
||||
description="Pause until a specific event occurs"
|
||||
/>
|
||||
<SelectItemWithDescription
|
||||
value="CONDITION"
|
||||
title="Condition"
|
||||
description="If/else branching based on contact data"
|
||||
/>
|
||||
<SelectItemWithDescription
|
||||
value="WEBHOOK"
|
||||
title="Webhook"
|
||||
description="Call an external API endpoint"
|
||||
/>
|
||||
<SelectItemWithDescription
|
||||
value="UPDATE_CONTACT"
|
||||
title="Update Contact"
|
||||
description="Modify contact data fields"
|
||||
/>
|
||||
<SelectItemWithDescription value="EXIT" title="Exit" description="End the workflow for this contact" />
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-neutral-500 mt-1">
|
||||
|
||||
Reference in New Issue
Block a user