import { Alert, AlertDescription, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, ConfirmDialog, IconSpinner, } from '@plunk/ui'; import type {Segment} from '@plunk/db'; import type {FilterCondition} from '@plunk/types'; 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, Trash2, Users} from 'lucide-react'; import {NextSeo} from 'next-seo'; import Link from 'next/link'; import {useState} from 'react'; import {toast} from 'sonner'; import useSWR from 'swr'; import dayjs from 'dayjs'; // Helper function to count total filters in a condition function countFiltersInCondition(condition: unknown): number { if (!condition || typeof condition !== 'object') return 0; const cond = condition as FilterCondition; if (!cond.groups || !Array.isArray(cond.groups)) return 0; return cond.groups.reduce((total, group) => { let count = group.filters?.length || 0; // Recursively count nested conditions if (group.conditions) { count += countFiltersInCondition(group.conditions); } return total + count; }, 0); } export default function SegmentsPage() { // Limit to 50 segments to avoid loading thousands into the browser const { data: segments, mutate, isLoading, } = useSWR('/segments', { revalidateOnFocus: false, }); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [segmentToDelete, setSegmentToDelete] = useState(null); // Show warning if there are many segments const showLimitWarning = segments && segments.length >= 50; const handleDelete = async () => { if (!segmentToDelete) return; try { await network.fetch('DELETE', `/segments/${segmentToDelete}`); toast.success('Segment deleted successfully'); void mutate(); } catch (error) { toast.error(error instanceof Error ? error.message : 'Failed to delete segment'); } finally { setSegmentToDelete(null); } }; return ( <>
{/* Header */}

Segments

Create dynamic audience groups based on contact attributes and behaviors

{/* Warning if too many segments */} {showLimitWarning && ( Showing first {segments?.length} segments. Consider archiving old segments to improve performance. )} {/* Segments Grid */} {isLoading ? (
) : segments?.length === 0 ? ( } /> ) : (
{segments?.map(segment => (
{segment.name} {(segment as unknown as {type: string}).type === 'STATIC' ? 'Static' : 'Dynamic'}
{segment.description && ( {segment.description} )}
{/* Stats */}
Members
{segment.memberCount}
{(segment as unknown as {type: string}).type !== 'STATIC' && (
Filters
{countFiltersInCondition(segment.condition)}
)} {/* Actions */}
{/* Metadata */}
Created {formatRelativeTime(segment.createdAt)}
{dayjs(segment.createdAt).format('DD MMMM YYYY, hh:mm')}
• Updated {formatRelativeTime(segment.updatedAt)}
{dayjs(segment.updatedAt).format('DD MMMM YYYY, hh:mm')}
))}
)}
); }