117 lines
3.6 KiB
TypeScript
117 lines
3.6 KiB
TypeScript
'use client'
|
|
|
|
import { createContext, useContext, useMemo, useState } from 'react'
|
|
import { Input } from '@/components/ui/input'
|
|
import { cn } from '@/lib/utils'
|
|
import {
|
|
useAutomationSuggestions,
|
|
type AutomationSuggestion,
|
|
} from '@/lib/mail-automation/use-automation-suggestions'
|
|
import type { AutomationSuggestionKind } from '@/lib/mail-automation/condition-helpers'
|
|
|
|
interface AutomationSuggestionsContextValue {
|
|
suggestionsFor: (kind: AutomationSuggestionKind) => AutomationSuggestion[]
|
|
}
|
|
|
|
const AutomationSuggestionsContext = createContext<AutomationSuggestionsContextValue | null>(null)
|
|
|
|
export function AutomationSuggestionsProvider({ children }: { children: React.ReactNode }) {
|
|
const { suggestionsFor } = useAutomationSuggestions()
|
|
const value = useMemo(() => ({ suggestionsFor }), [suggestionsFor])
|
|
return (
|
|
<AutomationSuggestionsContext.Provider value={value}>
|
|
{children}
|
|
</AutomationSuggestionsContext.Provider>
|
|
)
|
|
}
|
|
|
|
function useAutomationSuggestionsContext() {
|
|
return useContext(AutomationSuggestionsContext)
|
|
}
|
|
|
|
interface AutomationSuggestInputProps {
|
|
kind: AutomationSuggestionKind
|
|
value: string
|
|
onChange: (value: string) => void
|
|
placeholder?: string
|
|
className?: string
|
|
disabled?: boolean
|
|
}
|
|
|
|
export function AutomationSuggestInput({
|
|
kind,
|
|
value,
|
|
onChange,
|
|
placeholder,
|
|
className,
|
|
disabled,
|
|
}: AutomationSuggestInputProps) {
|
|
const ctx = useAutomationSuggestionsContext()
|
|
const [listId] = useState(() => `automation-suggest-${kind}-${Math.random().toString(36).slice(2, 9)}`)
|
|
const suggestions = kind === 'none' || !ctx ? [] : ctx.suggestionsFor(kind)
|
|
const filtered = useMemo(() => {
|
|
if (!value.trim()) return suggestions.slice(0, 40)
|
|
const q = value.trim().toLowerCase()
|
|
return suggestions
|
|
.filter(
|
|
(s) =>
|
|
s.value.toLowerCase().includes(q) ||
|
|
(s.label?.toLowerCase().includes(q) ?? false)
|
|
)
|
|
.slice(0, 40)
|
|
}, [suggestions, value])
|
|
|
|
if (kind === 'none' || suggestions.length === 0) {
|
|
return (
|
|
<Input
|
|
className={cn('h-8 text-xs', className)}
|
|
value={value}
|
|
placeholder={placeholder}
|
|
disabled={disabled}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
/>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="relative">
|
|
<Input
|
|
className={cn('h-8 text-xs', className)}
|
|
value={value}
|
|
placeholder={placeholder}
|
|
disabled={disabled}
|
|
list={listId}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
/>
|
|
<datalist id={listId}>
|
|
{filtered.map((s) => (
|
|
<option key={`${s.value}-${s.label ?? ''}`} value={s.value}>
|
|
{s.label && s.label !== s.value ? s.label : undefined}
|
|
</option>
|
|
))}
|
|
</datalist>
|
|
{filtered.length > 0 && value.trim() && !disabled ? (
|
|
<ul className="absolute z-20 mt-1 max-h-40 w-full overflow-y-auto rounded-md border border-border bg-popover py-1 shadow-md">
|
|
{filtered.slice(0, 8).map((s) => (
|
|
<li key={`pick-${s.value}-${s.label ?? ''}`}>
|
|
<button
|
|
type="button"
|
|
className="w-full px-2 py-1.5 text-left text-xs hover:bg-muted"
|
|
onMouseDown={(e) => {
|
|
e.preventDefault()
|
|
onChange(s.value)
|
|
}}
|
|
>
|
|
<span className="block truncate">{s.label ?? s.value}</span>
|
|
{s.label && s.label !== s.value ? (
|
|
<span className="block truncate font-mono text-[10px] text-muted-foreground">{s.value}</span>
|
|
) : null}
|
|
</button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|