Fix build errors
This commit is contained in:
@@ -132,135 +132,135 @@ export function DashboardLayout({children}: DashboardLayoutProps) {
|
|||||||
}, [handleLogout]);
|
}, [handleLogout]);
|
||||||
|
|
||||||
// Sidebar content (reusable for both desktop and mobile)
|
// Sidebar content (reusable for both desktop and mobile)
|
||||||
const SidebarContent = () => (
|
const SidebarContent = () => (
|
||||||
<>
|
<>
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="h-16 flex items-center gap-2 px-6 border-b border-neutral-200">
|
<div className="h-16 flex items-center gap-2 px-6 border-b border-neutral-200">
|
||||||
<Image src="/assets/logo.png" alt="Plunk" width={28} height={28} className="rounded" />
|
<Image src="/assets/logo.png" alt="Plunk" width={28} height={28} className="rounded" />
|
||||||
<h1 className="text-xl font-bold text-neutral-900">Plunk</h1>
|
<h1 className="text-xl font-bold text-neutral-900">Plunk</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Project Switcher */}
|
{/* Project Switcher */}
|
||||||
<div className="p-4 border-b border-neutral-200">
|
<div className="p-4 border-b border-neutral-200">
|
||||||
<div className="relative" ref={projectMenuRef}>
|
<div className="relative" ref={projectMenuRef}>
|
||||||
<button
|
<button
|
||||||
onClick={handleToggleProjectMenu}
|
onClick={handleToggleProjectMenu}
|
||||||
className="w-full flex items-center justify-between px-3 py-2 text-sm rounded-lg hover:bg-neutral-50 transition-colors"
|
className="w-full flex items-center justify-between px-3 py-2 text-sm rounded-lg hover:bg-neutral-50 transition-colors"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2 flex-1 min-w-0">
|
<div className="flex items-center gap-2 flex-1 min-w-0">
|
||||||
<div className="h-8 w-8 rounded-lg bg-neutral-900 text-white flex items-center justify-center text-xs font-medium flex-shrink-0">
|
<div className="h-8 w-8 rounded-lg bg-neutral-900 text-white flex items-center justify-center text-xs font-medium flex-shrink-0">
|
||||||
{activeProject?.name.charAt(0).toUpperCase() || 'P'}
|
{activeProject?.name.charAt(0).toUpperCase() || 'P'}
|
||||||
|
</div>
|
||||||
|
<span className="font-medium text-neutral-900 truncate">{activeProject?.name || 'Select project'}</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="font-medium text-neutral-900 truncate">{activeProject?.name || 'Select project'}</span>
|
<ChevronDown className="h-4 w-4 text-neutral-500 flex-shrink-0" />
|
||||||
</div>
|
</button>
|
||||||
<ChevronDown className="h-4 w-4 text-neutral-500 flex-shrink-0" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* Project Dropdown */}
|
{/* Project Dropdown */}
|
||||||
{showProjectMenu && (
|
{showProjectMenu && (
|
||||||
<div className="absolute top-full left-0 right-0 mt-1 bg-white border border-neutral-200 rounded-lg shadow-lg z-50 py-1">
|
<div className="absolute top-full left-0 right-0 mt-1 bg-white border border-neutral-200 rounded-lg shadow-lg z-50 py-1">
|
||||||
{availableProjects.map(project => (
|
{availableProjects.map(project => (
|
||||||
<button
|
<button
|
||||||
key={project.id}
|
key={project.id}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveProject(project);
|
setActiveProject(project);
|
||||||
setShowProjectMenu(false);
|
setShowProjectMenu(false);
|
||||||
}}
|
}}
|
||||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-neutral-50 transition-colors"
|
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-neutral-50 transition-colors"
|
||||||
>
|
|
||||||
<div className="h-6 w-6 rounded bg-neutral-900 text-white flex items-center justify-center text-xs font-medium">
|
|
||||||
{project.name.charAt(0).toUpperCase()}
|
|
||||||
</div>
|
|
||||||
<span className="text-neutral-900">{project.name}</span>
|
|
||||||
{activeProject?.id === project.id && (
|
|
||||||
<div className="ml-auto h-1.5 w-1.5 rounded-full bg-neutral-900" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
<div className="border-t border-neutral-200 my-1" />
|
|
||||||
<Link
|
|
||||||
href="/projects/create"
|
|
||||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-neutral-50 transition-colors text-neutral-700"
|
|
||||||
>
|
|
||||||
<Plus className="h-4 w-4" />
|
|
||||||
<span>Create project</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Navigation */}
|
|
||||||
<nav className="flex-1 px-3 py-4 overflow-y-auto">
|
|
||||||
{navigation.map((section, sectionIndex) => (
|
|
||||||
<div key={sectionIndex} className={sectionIndex > 0 ? 'mt-6' : ''}>
|
|
||||||
{section.title && (
|
|
||||||
<p className="px-3 mb-2 text-xs font-semibold text-neutral-500 uppercase tracking-wider">
|
|
||||||
{section.title}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<div className="space-y-1">
|
|
||||||
{section.items.map(item => {
|
|
||||||
const isActive = router.pathname === item.href;
|
|
||||||
const Icon = item.icon;
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
key={item.name}
|
|
||||||
href={item.href}
|
|
||||||
onClick={() => setShowMobileMenu(false)}
|
|
||||||
className={`flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-neutral-700 ${
|
|
||||||
isActive ? 'bg-neutral-100' : 'hover:bg-neutral-50 hover:text-neutral-900'
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<Icon className="h-5 w-5" />
|
<div className="h-6 w-6 rounded bg-neutral-900 text-white flex items-center justify-center text-xs font-medium">
|
||||||
{item.name}
|
{project.name.charAt(0).toUpperCase()}
|
||||||
</Link>
|
</div>
|
||||||
);
|
<span className="text-neutral-900">{project.name}</span>
|
||||||
})}
|
{activeProject?.id === project.id && (
|
||||||
</div>
|
<div className="ml-auto h-1.5 w-1.5 rounded-full bg-neutral-900" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
<div className="border-t border-neutral-200 my-1" />
|
||||||
|
<Link
|
||||||
|
href="/projects/create"
|
||||||
|
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-neutral-50 transition-colors text-neutral-700"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
<span>Create project</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{/* Settings & User Menu */}
|
|
||||||
<div className="border-t border-neutral-200 p-3 space-y-1">
|
|
||||||
<Link
|
|
||||||
href="/settings"
|
|
||||||
onClick={() => setShowMobileMenu(false)}
|
|
||||||
className={`flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-neutral-700 ${
|
|
||||||
router.pathname.startsWith('/settings') ? 'bg-neutral-100' : 'hover:bg-neutral-50 hover:text-neutral-900'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Settings className="h-5 w-5" />
|
|
||||||
Settings
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<div className="relative" ref={userMenuRef}>
|
|
||||||
<button
|
|
||||||
onClick={handleToggleUserMenu}
|
|
||||||
className="w-full flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg text-neutral-700 hover:bg-neutral-50 hover:text-neutral-900 transition-colors"
|
|
||||||
>
|
|
||||||
<User className="h-5 w-5" />
|
|
||||||
<span className="flex-1 text-left truncate">{user?.email}</span>
|
|
||||||
<ChevronDown className="h-4 w-4 text-neutral-500" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* User Dropdown */}
|
|
||||||
{showUserMenu && (
|
|
||||||
<div className="absolute bottom-full left-0 right-0 mb-1 bg-white border border-neutral-200 rounded-lg shadow-lg z-50 py-1">
|
|
||||||
<button
|
|
||||||
onClick={handleLogoutClick}
|
|
||||||
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-neutral-50 transition-colors text-red-600"
|
|
||||||
>
|
|
||||||
<LogOut className="h-4 w-4" />
|
|
||||||
<span>Log out</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</>
|
{/* Navigation */}
|
||||||
);
|
<nav className="flex-1 px-3 py-4 overflow-y-auto">
|
||||||
|
{navigation.map((section, sectionIndex) => (
|
||||||
|
<div key={sectionIndex} className={sectionIndex > 0 ? 'mt-6' : ''}>
|
||||||
|
{section.title && (
|
||||||
|
<p className="px-3 mb-2 text-xs font-semibold text-neutral-500 uppercase tracking-wider">
|
||||||
|
{section.title}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
<div className="space-y-1">
|
||||||
|
{section.items.map(item => {
|
||||||
|
const isActive = router.pathname === item.href;
|
||||||
|
const Icon = item.icon;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
href={item.href}
|
||||||
|
onClick={() => setShowMobileMenu(false)}
|
||||||
|
className={`flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-neutral-700 ${
|
||||||
|
isActive ? 'bg-neutral-100' : 'hover:bg-neutral-50 hover:text-neutral-900'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Icon className="h-5 w-5" />
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{/* Settings & User Menu */}
|
||||||
|
<div className="border-t border-neutral-200 p-3 space-y-1">
|
||||||
|
<Link
|
||||||
|
href="/settings"
|
||||||
|
onClick={() => setShowMobileMenu(false)}
|
||||||
|
className={`flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-neutral-700 ${
|
||||||
|
router.pathname.startsWith('/settings') ? 'bg-neutral-100' : 'hover:bg-neutral-50 hover:text-neutral-900'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Settings className="h-5 w-5" />
|
||||||
|
Settings
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className="relative" ref={userMenuRef}>
|
||||||
|
<button
|
||||||
|
onClick={handleToggleUserMenu}
|
||||||
|
className="w-full flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-lg text-neutral-700 hover:bg-neutral-50 hover:text-neutral-900 transition-colors"
|
||||||
|
>
|
||||||
|
<User className="h-5 w-5" />
|
||||||
|
<span className="flex-1 text-left truncate">{user?.email}</span>
|
||||||
|
<ChevronDown className="h-4 w-4 text-neutral-500" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* User Dropdown */}
|
||||||
|
{showUserMenu && (
|
||||||
|
<div className="absolute bottom-full left-0 right-0 mb-1 bg-white border border-neutral-200 rounded-lg shadow-lg z-50 py-1">
|
||||||
|
<button
|
||||||
|
onClick={handleLogoutClick}
|
||||||
|
className="w-full flex items-center gap-2 px-3 py-2 text-sm hover:bg-neutral-50 transition-colors text-red-600"
|
||||||
|
>
|
||||||
|
<LogOut className="h-4 w-4" />
|
||||||
|
<span>Log out</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen bg-neutral-50">
|
<div className="flex h-screen bg-neutral-50">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ interface EmailEditorProps {
|
|||||||
value: string;
|
value: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
canUploadImages?: boolean;
|
|
||||||
// Optional props for preview header
|
// Optional props for preview header
|
||||||
subject?: string;
|
subject?: string;
|
||||||
from?: string;
|
from?: string;
|
||||||
|
|||||||
@@ -119,6 +119,44 @@ const FilterRow = memo(function FilterRow({filter, onChange, onRemove, available
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
|
|
||||||
|
// Helper to get default value based on field type
|
||||||
|
const getDefaultValueForType = useCallback((type: string) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'boolean':
|
||||||
|
return true;
|
||||||
|
case 'number':
|
||||||
|
return 0;
|
||||||
|
case 'date':
|
||||||
|
return '';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Helper to get valid operators for a field type
|
||||||
|
const getOperatorsForType = useCallback((type: string, isEvent: boolean) => {
|
||||||
|
if (isEvent) {
|
||||||
|
return EVENT_OPERATORS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'boolean') {
|
||||||
|
return STANDARD_OPERATORS.filter(op =>
|
||||||
|
['equals', 'notEquals', 'exists', 'notExists'].includes(op.value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'number' || type === 'date') {
|
||||||
|
return STANDARD_OPERATORS.filter(op =>
|
||||||
|
['equals', 'notEquals', 'greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual', 'exists', 'notExists', 'within'].includes(op.value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// String type - no within operator, no comparison operators
|
||||||
|
return STANDARD_OPERATORS.filter(op =>
|
||||||
|
['equals', 'notEquals', 'contains', 'notContains', 'exists', 'notExists'].includes(op.value)
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const needsValue = !['exists', 'notExists', 'triggered', 'notTriggered'].includes(filter.operator);
|
const needsValue = !['exists', 'notExists', 'triggered', 'notTriggered'].includes(filter.operator);
|
||||||
const needsUnit = ['within', 'triggeredWithin'].includes(filter.operator);
|
const needsUnit = ['within', 'triggeredWithin'].includes(filter.operator);
|
||||||
|
|
||||||
@@ -208,45 +246,7 @@ const FilterRow = memo(function FilterRow({filter, onChange, onRemove, available
|
|||||||
|
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
setSearch('');
|
setSearch('');
|
||||||
}, [availableFields, filter.operator, fieldType, onChange]);
|
}, [availableFields, filter.operator, fieldType, onChange, getDefaultValueForType, getOperatorsForType]);
|
||||||
|
|
||||||
// Helper to get default value based on field type
|
|
||||||
const getDefaultValueForType = useCallback((type: string) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'boolean':
|
|
||||||
return true;
|
|
||||||
case 'number':
|
|
||||||
return 0;
|
|
||||||
case 'date':
|
|
||||||
return '';
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Helper to get valid operators for a field type
|
|
||||||
const getOperatorsForType = useCallback((type: string, isEvent: boolean) => {
|
|
||||||
if (isEvent) {
|
|
||||||
return EVENT_OPERATORS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'boolean') {
|
|
||||||
return STANDARD_OPERATORS.filter(op =>
|
|
||||||
['equals', 'notEquals', 'exists', 'notExists'].includes(op.value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'number' || type === 'date') {
|
|
||||||
return STANDARD_OPERATORS.filter(op =>
|
|
||||||
['equals', 'notEquals', 'greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual', 'exists', 'notExists', 'within'].includes(op.value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// String type - no within operator, no comparison operators
|
|
||||||
return STANDARD_OPERATORS.filter(op =>
|
|
||||||
['equals', 'notEquals', 'contains', 'notContains', 'exists', 'notExists'].includes(op.value)
|
|
||||||
);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Get label for selected field
|
// Get label for selected field
|
||||||
const getFieldLabel = useCallback(() => {
|
const getFieldLabel = useCallback(() => {
|
||||||
|
|||||||
@@ -334,6 +334,18 @@ export function WorkflowBuilder({workflowId, steps, onUpdate}: WorkflowBuilderPr
|
|||||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||||
const [stepToDelete, setStepToDelete] = useState<string | null>(null);
|
const [stepToDelete, setStepToDelete] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// Define handlers before they are used in useMemo
|
||||||
|
const handleEditStep = useCallback((stepId: string) => {
|
||||||
|
// This will be handled by the parent component
|
||||||
|
const event = new CustomEvent('workflow-edit-step', {detail: {stepId}});
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleDeleteStepClick = useCallback((stepId: string) => {
|
||||||
|
setStepToDelete(stepId);
|
||||||
|
setShowDeleteDialog(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Convert workflow steps to React Flow nodes
|
// Convert workflow steps to React Flow nodes
|
||||||
|
|
||||||
const rawNodes: Node[] = useMemo(() => {
|
const rawNodes: Node[] = useMemo(() => {
|
||||||
@@ -622,17 +634,6 @@ export function WorkflowBuilder({workflowId, steps, onUpdate}: WorkflowBuilderPr
|
|||||||
[addStepContext, workflowId, onUpdate],
|
[addStepContext, workflowId, onUpdate],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleEditStep = useCallback((stepId: string) => {
|
|
||||||
// This will be handled by the parent component
|
|
||||||
const event = new CustomEvent('workflow-edit-step', {detail: {stepId}});
|
|
||||||
window.dispatchEvent(event);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleDeleteStepClick = useCallback((stepId: string) => {
|
|
||||||
setStepToDelete(stepId);
|
|
||||||
setShowDeleteDialog(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Get all steps that will be affected by deleting a step (the step itself + all downstream steps)
|
// Get all steps that will be affected by deleting a step (the step itself + all downstream steps)
|
||||||
const getAffectedSteps = useCallback(
|
const getAffectedSteps = useCallback(
|
||||||
(stepId: string): typeof steps => {
|
(stepId: string): typeof steps => {
|
||||||
|
|||||||
@@ -546,10 +546,6 @@ export default function CampaignDetailsPage() {
|
|||||||
setHasChanges(true);
|
setHasChanges(true);
|
||||||
}}
|
}}
|
||||||
placeholder="<h1>Welcome!</h1><p>Your email content here...</p>"
|
placeholder="<h1>Welcome!</h1><p>Your email content here...</p>"
|
||||||
canUploadImages={true}
|
|
||||||
subject={editedCampaign.subject}
|
|
||||||
from={editedCampaign.from}
|
|
||||||
replyTo={editedCampaign.replyTo || undefined}
|
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -195,10 +195,6 @@ export default function CreateCampaignPage() {
|
|||||||
value={body}
|
value={body}
|
||||||
onChange={setBody}
|
onChange={setBody}
|
||||||
placeholder="<h1>Welcome!</h1><p>Your email content here...</p>"
|
placeholder="<h1>Welcome!</h1><p>Your email content here...</p>"
|
||||||
canUploadImages={true}
|
|
||||||
subject={subject}
|
|
||||||
from={from}
|
|
||||||
replyTo={replyTo}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@@ -270,10 +270,6 @@ export default function TemplateEditorPage() {
|
|||||||
value={editedTemplate.body || ''}
|
value={editedTemplate.body || ''}
|
||||||
onChange={body => setEditedTemplate({...editedTemplate, body})}
|
onChange={body => setEditedTemplate({...editedTemplate, body})}
|
||||||
placeholder="<h1>Welcome!</h1><p>Thanks for subscribing to our newsletter.</p>"
|
placeholder="<h1>Welcome!</h1><p>Thanks for subscribing to our newsletter.</p>"
|
||||||
canUploadImages={true}
|
|
||||||
subject={editedTemplate.subject}
|
|
||||||
from={editedTemplate.from}
|
|
||||||
replyTo={editedTemplate.replyTo || undefined}
|
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -179,10 +179,6 @@ export default function CreateTemplatePage() {
|
|||||||
value={body}
|
value={body}
|
||||||
onChange={setBody}
|
onChange={setBody}
|
||||||
placeholder="<h1>Welcome!</h1><p>Thanks for subscribing to our newsletter.</p>"
|
placeholder="<h1>Welcome!</h1><p>Thanks for subscribing to our newsletter.</p>"
|
||||||
canUploadImages={true}
|
|
||||||
subject={subject}
|
|
||||||
from={from}
|
|
||||||
replyTo={replyTo}
|
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
import "./.next/dev/types/routes.d.ts";
|
import "./.next/types/routes.d.ts";
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ const DialogContent = React.forwardRef<
|
|||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-neutral-200 bg-white p-4 sm:p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-lg max-h-[90vh] overflow-y-auto mx-4 sm:mx-0',
|
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-neutral-200 bg-white p-4 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg max-h-[90vh] overflow-y-auto',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<DialogPrimitive.Close className="absolute right-3 top-3 sm:right-4 sm:top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-500 p-1 sm:p-0">
|
<DialogPrimitive.Close className="absolute right-2.5 top-2.5 sm:right-4 sm:top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-neutral-100 data-[state=open]:text-neutral-500 p-1 sm:p-1.5">
|
||||||
<X className="h-5 w-5 sm:h-4 sm:w-4" />
|
<X className="h-5 w-5 sm:h-5 sm:w-5" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</DialogPrimitive.Close>
|
</DialogPrimitive.Close>
|
||||||
</DialogPrimitive.Content>
|
</DialogPrimitive.Content>
|
||||||
|
|||||||
Reference in New Issue
Block a user