@startsimpli/ui 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/src/__mocks__/next/link.js +11 -0
- package/src/components/ActivityTimeline.tsx +173 -0
- package/src/components/LogActivityDialog.tsx +303 -0
- package/src/components/QuickLogButtons.tsx +32 -0
- package/src/components/account/__tests__/account.test.tsx +315 -0
- package/src/components/badge/StageBadge.tsx +31 -0
- package/src/components/badge/index.ts +3 -0
- package/src/components/command-palette/CommandGroup.tsx +23 -0
- package/src/components/command-palette/CommandPalette.tsx +327 -0
- package/src/components/command-palette/CommandResultItem.tsx +59 -0
- package/src/components/command-palette/__tests__/CommandGroup.test.tsx +81 -0
- package/src/components/command-palette/__tests__/CommandResultItem.test.tsx +166 -0
- package/src/components/command-palette/__tests__/command-palette-context.test.tsx +166 -0
- package/src/components/command-palette/__tests__/useCommandPaletteSearch.test.ts +271 -0
- package/src/components/command-palette/command-palette-context.tsx +51 -0
- package/src/components/command-palette/index.ts +9 -0
- package/src/components/command-palette/useCommandPaletteSearch.ts +114 -0
- package/src/components/compose/__tests__/compose.test.tsx +656 -0
- package/src/components/compose/compose-header.tsx +72 -0
- package/src/components/compose/compose-loading.tsx +13 -0
- package/src/components/compose/index.ts +6 -0
- package/src/components/compose/save-status-indicator.tsx +57 -0
- package/src/components/compose/send-confirmation-dialog.tsx +87 -0
- package/src/components/compose/subject-input.tsx +25 -0
- package/src/components/compose/useAutoSave.ts +93 -0
- package/src/components/dashboard/DashboardGrid.tsx +32 -0
- package/src/components/dashboard/DashboardSection.tsx +32 -0
- package/src/components/dashboard/MetricCard.tsx +129 -0
- package/src/components/dashboard/PeriodSelector.tsx +55 -0
- package/src/components/dashboard/PipelineFunnel.tsx +126 -0
- package/src/components/dashboard/SparklineTrend.tsx +102 -0
- package/src/components/dashboard/TopCampaigns.tsx +132 -0
- package/src/components/dashboard/__tests__/dashboard.test.tsx +785 -0
- package/src/components/dashboard/index.ts +20 -0
- package/src/components/dialog/ConfirmDialog.tsx +72 -0
- package/src/components/dialog/__tests__/ConfirmDialog.test.tsx +126 -0
- package/src/components/dialog/index.ts +3 -0
- package/src/components/email-dialogs/__tests__/email-dialogs.test.tsx +982 -0
- package/src/components/email-dialogs/index.ts +14 -0
- package/src/components/email-dialogs/merge-fields.tsx +196 -0
- package/src/components/email-dialogs/preview-dialog.tsx +194 -0
- package/src/components/email-dialogs/schedule-dialog.tsx +297 -0
- package/src/components/email-dialogs/template-picker.tsx +225 -0
- package/src/components/email-dialogs/test-send-dialog.tsx +188 -0
- package/src/components/email-editor/BlockRenderer.tsx +120 -0
- package/src/components/email-editor/__tests__/BlockRenderer.test.tsx +332 -0
- package/src/components/email-editor/__tests__/block-renderers.test.ts +624 -0
- package/src/components/email-editor/__tests__/email-html-renderer.test.ts +376 -0
- package/src/components/email-editor/add-block-menu.tsx +151 -0
- package/src/components/email-editor/block-toolbar.tsx +73 -0
- package/src/components/email-editor/blocks/__tests__/blocks.test.tsx +818 -0
- package/src/components/email-editor/blocks/button-block.tsx +44 -0
- package/src/components/email-editor/blocks/divider-block.tsx +43 -0
- package/src/components/email-editor/blocks/footer-block.tsx +39 -0
- package/src/components/email-editor/blocks/header-block.tsx +39 -0
- package/src/components/email-editor/blocks/image-block.tsx +61 -0
- package/src/components/email-editor/blocks/index.ts +9 -0
- package/src/components/email-editor/blocks/metrics-block.tsx +198 -0
- package/src/components/email-editor/blocks/social-block.tsx +75 -0
- package/src/components/email-editor/blocks/spacer-block.tsx +26 -0
- package/src/components/email-editor/blocks/text-block.tsx +75 -0
- package/src/components/email-editor/editor-sidebar.tsx +66 -0
- package/src/components/email-editor/email-editor.tsx +497 -0
- package/src/components/email-editor/hooks/__tests__/useDragDrop.test.ts +355 -0
- package/src/components/email-editor/hooks/__tests__/useEmailEditorState.test.ts +551 -0
- package/src/components/email-editor/hooks/useDragDrop.ts +181 -0
- package/src/components/email-editor/hooks/useEmailEditorState.ts +426 -0
- package/src/components/email-editor/index.ts +51 -0
- package/src/components/email-editor/panels/BlockPropertyPanel.tsx +637 -0
- package/src/components/email-editor/panels/GlobalStylesPanel.tsx +108 -0
- package/src/components/email-editor/panels/SectionSettingsPanel.tsx +80 -0
- package/src/components/email-editor/panels/__tests__/BlockPropertyPanel.test.tsx +707 -0
- package/src/components/email-editor/panels/__tests__/GlobalStylesPanel.test.tsx +226 -0
- package/src/components/email-editor/panels/index.ts +3 -0
- package/src/components/email-editor/renderer/block-renderers.ts +209 -0
- package/src/components/email-editor/renderer/email-html-renderer.ts +128 -0
- package/src/components/email-editor/types.ts +413 -0
- package/src/components/email-editor/utils/defaults.ts +116 -0
- package/src/components/email-editor/utils/undo-redo.ts +59 -0
- package/src/components/enrichment/EnrichButton.tsx +33 -0
- package/src/components/enrichment/EnrichmentProgress.tsx +66 -0
- package/src/components/enrichment/QualityBadge.tsx +43 -0
- package/src/components/enrichment/__tests__/enrichment.test.tsx +184 -0
- package/src/components/enrichment/index.ts +8 -0
- package/src/components/gantt/GanttBoardView.tsx +71 -0
- package/src/components/gantt/GanttChart.tsx +140 -887
- package/src/components/gantt/GanttFilterBar.tsx +100 -0
- package/src/components/gantt/GanttListView.tsx +63 -0
- package/src/components/gantt/GanttTimelineView.tsx +215 -0
- package/src/components/gantt/__tests__/GanttBoardView.test.tsx +305 -0
- package/src/components/gantt/__tests__/GanttFilterBar.test.tsx +544 -0
- package/src/components/gantt/__tests__/GanttListView.test.tsx +337 -0
- package/src/components/gantt/__tests__/GanttTimelineView.test.tsx +375 -0
- package/src/components/gantt/__tests__/gantt-utils.test.ts +341 -0
- package/src/components/gantt/__tests__/useGanttState.test.ts +535 -0
- package/src/components/gantt/hooks/useGanttState.ts +644 -0
- package/src/components/gantt/index.ts +10 -0
- package/src/components/gantt/types.ts +5 -5
- package/src/components/index.ts +46 -0
- package/src/components/integrations/ConnectionStatus.tsx +77 -0
- package/src/components/integrations/IntegrationCard.tsx +92 -0
- package/src/components/integrations/__tests__/integrations.test.tsx +191 -0
- package/src/components/integrations/index.ts +5 -0
- package/src/components/kanban/KanbanBoard.tsx +103 -0
- package/src/components/kanban/__tests__/kanban.test.tsx +157 -0
- package/src/components/kanban/index.ts +2 -0
- package/src/components/lists/CreateListDialog.tsx +158 -0
- package/src/components/lists/ListCard.tsx +77 -0
- package/src/components/lists/__tests__/lists.test.tsx +263 -0
- package/src/components/lists/index.ts +5 -0
- package/src/components/loading/__tests__/loading.test.tsx +114 -0
- package/src/components/navigation/__tests__/navigation.test.tsx +194 -0
- package/src/components/pipeline/StageTransitionModal.tsx +146 -0
- package/src/components/pipeline/__tests__/pipeline.test.tsx +169 -0
- package/src/components/pipeline/index.ts +2 -0
- package/src/components/settings/SettingsCard.tsx +33 -0
- package/src/components/settings/SettingsLayout.tsx +28 -0
- package/src/components/settings/SettingsNav.tsx +42 -0
- package/src/components/settings/__tests__/settings.test.tsx +181 -0
- package/src/components/settings/index.ts +6 -0
- package/src/components/wizard/__tests__/wizard.test.tsx +97 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { MetricCard } from './MetricCard'
|
|
2
|
+
export type { MetricCardProps, MetricCardTrend } from './MetricCard'
|
|
3
|
+
|
|
4
|
+
export { PeriodSelector } from './PeriodSelector'
|
|
5
|
+
export type { PeriodSelectorProps, PeriodOption } from './PeriodSelector'
|
|
6
|
+
|
|
7
|
+
export { SparklineTrend } from './SparklineTrend'
|
|
8
|
+
export type { SparklineTrendProps } from './SparklineTrend'
|
|
9
|
+
|
|
10
|
+
export { DashboardGrid } from './DashboardGrid'
|
|
11
|
+
export type { DashboardGridProps } from './DashboardGrid'
|
|
12
|
+
|
|
13
|
+
export { DashboardSection } from './DashboardSection'
|
|
14
|
+
export type { DashboardSectionProps } from './DashboardSection'
|
|
15
|
+
|
|
16
|
+
export { PipelineFunnel } from './PipelineFunnel'
|
|
17
|
+
export type { PipelineFunnelProps, FunnelStage } from './PipelineFunnel'
|
|
18
|
+
|
|
19
|
+
export { TopCampaigns } from './TopCampaigns'
|
|
20
|
+
export type { TopCampaignsProps, TopCampaign } from './TopCampaigns'
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { Loader2 } from 'lucide-react'
|
|
5
|
+
import { BaseDialog } from './BaseDialog'
|
|
6
|
+
import { Button } from '../ui/button'
|
|
7
|
+
|
|
8
|
+
export interface ConfirmDialogProps {
|
|
9
|
+
open: boolean
|
|
10
|
+
onClose: () => void
|
|
11
|
+
onConfirm: () => void | Promise<void>
|
|
12
|
+
title: string
|
|
13
|
+
description?: string | React.ReactNode
|
|
14
|
+
confirmLabel?: string
|
|
15
|
+
cancelLabel?: string
|
|
16
|
+
destructive?: boolean
|
|
17
|
+
loading?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function ConfirmDialog({
|
|
21
|
+
open,
|
|
22
|
+
onClose,
|
|
23
|
+
onConfirm,
|
|
24
|
+
title,
|
|
25
|
+
description,
|
|
26
|
+
confirmLabel = 'Confirm',
|
|
27
|
+
cancelLabel = 'Cancel',
|
|
28
|
+
destructive = false,
|
|
29
|
+
loading = false,
|
|
30
|
+
}: ConfirmDialogProps) {
|
|
31
|
+
const handleClose = () => {
|
|
32
|
+
if (loading) return
|
|
33
|
+
onClose()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<BaseDialog
|
|
38
|
+
open={open}
|
|
39
|
+
onOpenChange={(o) => {
|
|
40
|
+
if (!o) handleClose()
|
|
41
|
+
}}
|
|
42
|
+
size="sm"
|
|
43
|
+
loading={loading}
|
|
44
|
+
>
|
|
45
|
+
<BaseDialog.Header>
|
|
46
|
+
<BaseDialog.Title>{title}</BaseDialog.Title>
|
|
47
|
+
</BaseDialog.Header>
|
|
48
|
+
{description && (
|
|
49
|
+
<BaseDialog.Body>
|
|
50
|
+
{typeof description === 'string' ? (
|
|
51
|
+
<p className="text-sm text-gray-600">{description}</p>
|
|
52
|
+
) : (
|
|
53
|
+
description
|
|
54
|
+
)}
|
|
55
|
+
</BaseDialog.Body>
|
|
56
|
+
)}
|
|
57
|
+
<BaseDialog.Footer>
|
|
58
|
+
<Button variant="outline" onClick={handleClose} disabled={loading}>
|
|
59
|
+
{cancelLabel}
|
|
60
|
+
</Button>
|
|
61
|
+
<Button
|
|
62
|
+
variant={destructive ? 'destructive' : 'default'}
|
|
63
|
+
onClick={onConfirm}
|
|
64
|
+
disabled={loading}
|
|
65
|
+
>
|
|
66
|
+
{loading && <Loader2 className="w-4 h-4 animate-spin mr-2" />}
|
|
67
|
+
{confirmLabel}
|
|
68
|
+
</Button>
|
|
69
|
+
</BaseDialog.Footer>
|
|
70
|
+
</BaseDialog>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
2
|
+
import { ConfirmDialog } from '../ConfirmDialog'
|
|
3
|
+
|
|
4
|
+
// Mock Radix portal to render inline
|
|
5
|
+
jest.mock('@radix-ui/react-dialog', () => {
|
|
6
|
+
const actual = jest.requireActual('@radix-ui/react-dialog')
|
|
7
|
+
return {
|
|
8
|
+
...actual,
|
|
9
|
+
Portal: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe('ConfirmDialog', () => {
|
|
14
|
+
const defaultProps = {
|
|
15
|
+
open: true,
|
|
16
|
+
onClose: jest.fn(),
|
|
17
|
+
onConfirm: jest.fn(),
|
|
18
|
+
title: 'Delete item?',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
jest.clearAllMocks()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('renders title', () => {
|
|
26
|
+
render(<ConfirmDialog {...defaultProps} />)
|
|
27
|
+
expect(screen.getByText('Delete item?')).toBeInTheDocument()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('renders string description', () => {
|
|
31
|
+
render(
|
|
32
|
+
<ConfirmDialog {...defaultProps} description="This cannot be undone." />
|
|
33
|
+
)
|
|
34
|
+
expect(screen.getByText('This cannot be undone.')).toBeInTheDocument()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('renders ReactNode description', () => {
|
|
38
|
+
render(
|
|
39
|
+
<ConfirmDialog
|
|
40
|
+
{...defaultProps}
|
|
41
|
+
description={<span data-testid="custom">Custom content</span>}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
expect(screen.getByTestId('custom')).toBeInTheDocument()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('renders default button labels', () => {
|
|
48
|
+
render(<ConfirmDialog {...defaultProps} />)
|
|
49
|
+
expect(screen.getByText('Confirm')).toBeInTheDocument()
|
|
50
|
+
expect(screen.getByText('Cancel')).toBeInTheDocument()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('renders custom button labels', () => {
|
|
54
|
+
render(
|
|
55
|
+
<ConfirmDialog
|
|
56
|
+
{...defaultProps}
|
|
57
|
+
confirmLabel="Delete"
|
|
58
|
+
cancelLabel="Keep"
|
|
59
|
+
/>
|
|
60
|
+
)
|
|
61
|
+
expect(screen.getByText('Delete')).toBeInTheDocument()
|
|
62
|
+
expect(screen.getByText('Keep')).toBeInTheDocument()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('calls onConfirm when confirm button is clicked', () => {
|
|
66
|
+
render(<ConfirmDialog {...defaultProps} />)
|
|
67
|
+
fireEvent.click(screen.getByText('Confirm'))
|
|
68
|
+
expect(defaultProps.onConfirm).toHaveBeenCalledTimes(1)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('calls onClose when cancel button is clicked', () => {
|
|
72
|
+
render(<ConfirmDialog {...defaultProps} />)
|
|
73
|
+
fireEvent.click(screen.getByText('Cancel'))
|
|
74
|
+
expect(defaultProps.onClose).toHaveBeenCalledTimes(1)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('disables buttons when loading', () => {
|
|
78
|
+
render(<ConfirmDialog {...defaultProps} loading />)
|
|
79
|
+
const confirmBtn = screen.getByText('Confirm').closest('button')
|
|
80
|
+
const cancelBtn = screen.getByText('Cancel').closest('button')
|
|
81
|
+
expect(confirmBtn).toBeDisabled()
|
|
82
|
+
expect(cancelBtn).toBeDisabled()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('shows spinner when loading', () => {
|
|
86
|
+
const { container } = render(<ConfirmDialog {...defaultProps} loading />)
|
|
87
|
+
const spinner = container.querySelector('.animate-spin')
|
|
88
|
+
expect(spinner).toBeInTheDocument()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('does not call onClose when cancel clicked during loading', () => {
|
|
92
|
+
render(<ConfirmDialog {...defaultProps} loading />)
|
|
93
|
+
// The button is disabled so the click handler won't fire,
|
|
94
|
+
// but also handleClose guards against it
|
|
95
|
+
const cancelBtn = screen.getByText('Cancel').closest('button')!
|
|
96
|
+
fireEvent.click(cancelBtn)
|
|
97
|
+
expect(defaultProps.onClose).not.toHaveBeenCalled()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('applies destructive variant to confirm button', () => {
|
|
101
|
+
render(<ConfirmDialog {...defaultProps} destructive />)
|
|
102
|
+
const confirmBtn = screen.getByText('Confirm').closest('button')
|
|
103
|
+
expect(confirmBtn?.className).toMatch(/destructive/)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('applies default variant when not destructive', () => {
|
|
107
|
+
render(<ConfirmDialog {...defaultProps} />)
|
|
108
|
+
const confirmBtn = screen.getByText('Confirm').closest('button')
|
|
109
|
+
expect(confirmBtn?.className).not.toMatch(/destructive/)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('does not render body when no description provided', () => {
|
|
113
|
+
const { container } = render(<ConfirmDialog {...defaultProps} />)
|
|
114
|
+
// Body has class py-4, should not exist
|
|
115
|
+
expect(container.querySelector('.py-4')).not.toBeInTheDocument()
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('handles async onConfirm', async () => {
|
|
119
|
+
const asyncConfirm = jest.fn().mockResolvedValue(undefined)
|
|
120
|
+
render(<ConfirmDialog {...defaultProps} onConfirm={asyncConfirm} />)
|
|
121
|
+
fireEvent.click(screen.getByText('Confirm'))
|
|
122
|
+
await waitFor(() => {
|
|
123
|
+
expect(asyncConfirm).toHaveBeenCalledTimes(1)
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
})
|