Selectors with description
This commit is contained in:
+4
-1
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user