Selectors with description

This commit is contained in:
Dries Augustyns
2025-12-07 14:20:44 +01:00
parent 683356c17c
commit c189175658
13 changed files with 282 additions and 141 deletions
@@ -7,6 +7,9 @@ CREATE TYPE "Role" AS ENUM ('OWNER', 'ADMIN', 'MEMBER');
-- CreateEnum
CREATE TYPE "TemplateType" AS ENUM ('TRANSACTIONAL', 'MARKETING');
-- CreateEnum
CREATE TYPE "TrackingMode" AS ENUM ('ENABLED', 'DISABLED', 'MARKETING_ONLY');
-- CreateEnum
CREATE TYPE "CampaignStatus" AS ENUM ('DRAFT', 'SCHEDULED', 'SENDING', 'SENT', 'CANCELLED');
@@ -55,7 +58,7 @@ CREATE TABLE "projects" (
"billingLimitWorkflows" INTEGER,
"billingLimitCampaigns" INTEGER,
"billingLimitTransactional" INTEGER,
"trackingEnabled" BOOLEAN NOT NULL DEFAULT true,
"tracking" "TrackingMode" NOT NULL DEFAULT 'ENABLED',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
+7 -1
View File
@@ -54,7 +54,7 @@ model Project {
billingLimitTransactional Int? // Max transactional emails per month
// Email Tracking
trackingEnabled Boolean @default(true) // Enable/disable open and click tracking
tracking TrackingMode @default(ENABLED) // Open and click tracking mode
// Relations
members Membership[]
@@ -636,6 +636,12 @@ enum TemplateType {
MARKETING
}
enum TrackingMode {
ENABLED // Track opens and clicks for all emails
DISABLED // No tracking for any emails
MARKETING_ONLY // Track only marketing/campaign emails, not transactional
}
enum CampaignStatus {
DRAFT
SCHEDULED
+3 -7
View File
@@ -1,4 +1,4 @@
import {CampaignAudienceType, TemplateType, WorkflowStepType, WorkflowTriggerType} from '@plunk/db';
import {CampaignAudienceType, TemplateType, TrackingMode, WorkflowStepType, WorkflowTriggerType} from '@plunk/db';
import {z} from 'zod';
const literalSchema = z.union([z.string(), z.number(), z.boolean(), z.null(), z.date()]);
@@ -59,7 +59,7 @@ export const ProjectSchemas = {
}),
update: z.object({
name: z.string().min(1).max(100).optional(),
trackingEnabled: z.boolean().optional(),
tracking: z.nativeEnum(TrackingMode).optional(),
}),
} as const;
@@ -223,11 +223,7 @@ export const WorkflowStepConfigSchemas = {
),
waitForEvent: z.object({
eventName: z.string().min(1),
timeout: z
.number()
.positive()
.max(31536000, 'Timeout cannot exceed 365 days (31,536,000 seconds)')
.optional(),
timeout: z.number().positive().max(31536000, 'Timeout cannot exceed 365 days (31,536,000 seconds)').optional(),
}),
condition: z.object({
field: z.string().min(1),
@@ -122,6 +122,37 @@ const SelectItem = React.forwardRef<
));
SelectItem.displayName = SelectPrimitive.Item.displayName;
interface SelectItemWithDescriptionProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {
title: string;
description?: string;
}
const SelectItemWithDescription = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
SelectItemWithDescriptionProps
>(({className, title, description, ...props}, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
'relative flex w-full cursor-default select-none items-start rounded-sm py-2.5 pl-8 pr-3 text-sm outline-none focus:bg-neutral-100 focus:text-neutral-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className,
)}
{...props}
>
<span className="absolute left-2 top-3 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<div className="flex flex-col gap-0.5">
<SelectPrimitive.ItemText className="font-medium">{title}</SelectPrimitive.ItemText>
{description && <span className="text-xs text-neutral-500 leading-tight">{description}</span>}
</div>
</SelectPrimitive.Item>
));
SelectItemWithDescription.displayName = 'SelectItemWithDescription';
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
@@ -138,6 +169,7 @@ export {
SelectContent,
SelectLabel,
SelectItem,
SelectItemWithDescription,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,