import React from 'react'; import { cn } from '../../lib/utils'; import type { VariantProps } from 'class-variance-authority'; import { badgeVariants } from './badge-variants'; import { motion, AnimatePresence } from "framer-motion"; import { Check, CheckCircle2, AlertCircle, Code2, Plus, Trash2 as Trash, Copy, Terminal, ArrowLeft, Settings, Save, ExternalLink, Crown, Download, Bold, Italic, AlignRight, Eraser, ChevronUp, ChevronDown, Smile, Type, Underline, Strikethrough, AlignLeft, AlignCenter, ShieldCheck, } from 'lucide-react'; import { Slot } from "radix-ui"; import { Switch } from './switch'; import { Badge } from './badge'; import { Input } from './input'; import { Label } from './label'; import { PhoneInput } from './PhoneInput'; import { toast } from "sonner"; import Editor from 'react-simple-code-editor'; import EmojiPicker, { EmojiStyle, Theme } from 'emoji-picker-react'; import { Popover, PopoverTrigger, PopoverContent } from './popover'; import NoticeBar from '../../NoticeBar'; // Fix for potentially problematic ESM import of Editor const EditorComponent = (Editor as unknown as { default: React.ComponentType> }).default || Editor; export { Badge }; export const SafeRender: React.FC<{ content: unknown, isHtml?: boolean }> = ({ content, isHtml = false }) => { if (content === null || content === undefined) return null; if (typeof content === 'string') { if (isHtml && (content.includes('<') || content.includes('&'))) return ; return <>{content}; } if (typeof content === 'number' || typeof content === 'boolean') { return <>{content.toString()}; } if (React.isValidElement(content)) { return content; } if (Array.isArray(content)) { return ( <> {content.map((item, i) => ( ))} ); } if (typeof content === 'object') { let str = '[Object]'; try { str = JSON.stringify(content); } catch { str = '[Complex Object]'; } return {str}; } return null; }; export const StatusMessage: React.FC<{ type: 'active' | 'inactive' | 'error' | 'warning', message: React.ReactNode, icon?: React.ComponentType<{ size?: number; className?: string }>, className?: string }> = ({ type, message, icon: Icon, className = "" }) => { if (!message) return null; const styles = { active: { text: 'text-emerald-600', bg: 'bg-emerald-50/50', border: 'border-emerald-100', icon: CheckCircle2 }, inactive: { text: 'text-slate-500', bg: 'bg-slate-50/50', border: 'border-slate-100', icon: AlertCircle }, error: { text: 'text-rose-600', bg: 'bg-rose-50/50', border: 'border-rose-100', icon: AlertCircle }, warning: { text: 'text-amber-600', bg: 'bg-amber-50/50', border: 'border-amber-100', icon: AlertCircle } }; const style = styles[type]; const DisplayIcon = Icon || style.icon; return (
); }; export const SectionHeader: React.FC<{ title: string; description?: string; className?: string; badge?: string; badgeColor?: 'blue' | 'brand' | 'emerald'; icon?: React.ComponentType<{ size?: number; className?: string; opacity?: number; shrink?: number | string; strokeWidth?: number }>; rightAction?: React.ReactNode; }> = ({ title, description = "", className = "", badge, badgeColor = 'brand', icon: Icon, rightAction }) => { const badgeStyles = { blue: 'bg-blue-50 text-blue-500 border-blue-100', brand: 'bg-[#004449]/10 text-[#004449] border-[#004449]/20', emerald: 'bg-emerald-50 text-emerald-500 border-emerald-100' }; return (
{Icon && } {badge && {badge}}
{description &&
}
{rightAction &&
{rightAction}
}
); }; export const SettingSwitchCard: React.FC<{ label: string; description: string; checked: boolean; onChange: (checked: boolean) => void; disabled?: boolean; className?: string; }> = ({ label, description, checked, onChange, disabled, className = "" }) => (
); const RichInput = React.forwardRef void; onBlur?: () => void; placeholder?: string; className?: string; hasIcon?: boolean; }>(({ value, onChange, onBlur, placeholder, className, hasIcon }, ref) => { const innerRef = React.useRef(null); // Use a ref to combine with the forwarded ref React.useImperativeHandle(ref, () => innerRef.current!); // Sync internal state with prop React.useLayoutEffect(() => { if (innerRef.current) { const currentHTML = innerRef.current.innerHTML; const targetHTML = value || ''; // Only update if they are truly different to prevent cursor jumps if (currentHTML !== targetHTML) { innerRef.current.innerHTML = targetHTML; } } }, [value]); const handleInput = () => { if (innerRef.current) { onChange(innerRef.current.innerHTML); } }; return (
{(!value || value === '
') && (
{placeholder}
)}
); }); RichInput.displayName = "RichInput"; export const SettingInput: React.FC<{ label: string; value: string; onChange: (val: string) => void; onBlur?: () => void; placeholder?: string; type?: string; icon?: React.ComponentType<{ size?: number; className?: string; strokeWidth?: number }>; description?: string; button?: React.ReactNode; className?: string; labelHidden?: boolean; showFormatting?: boolean; showEmoji?: boolean; disabled?: boolean; onFormat?: (type: 'bold' | 'italic' | 'underline' | 'strike' | 'align-left' | 'align-center' | 'align-right' | 'clear' | 'size-up' | 'size-down' | 'color' | 'emoji', value?: string) => void; }> = ({ label, value, onChange, onBlur, placeholder, type = 'text', icon: Icon, description, button, className = "", labelHidden = false, showFormatting = false, showEmoji = false, disabled = false, onFormat }) => { const inputRef = React.useRef(null); const isRTL = typeof document !== 'undefined' && document.dir === 'rtl'; const insertAtCursor = (text: string) => { if (!inputRef.current) return; if (showFormatting) { // ContentEditable insertion inputRef.current.focus(); const selection = window.getSelection(); if (selection && selection.rangeCount > 0) { const range = selection.getRangeAt(0); range.deleteContents(); const node = document.createTextNode(text); range.insertNode(node); range.setStartAfter(node); range.setEndAfter(node); selection.removeAllRanges(); selection.addRange(range); onChange(inputRef.current.innerHTML); } else { onChange(value + text); } return; } // Standard input insertion const input = inputRef.current as HTMLInputElement; const start = input?.selectionStart || 0; const end = input?.selectionEnd || 0; const newValue = value.substring(0, start) + text + value.substring(end); onChange(newValue); // Reset cursor position setTimeout(() => { if (inputRef.current) { const input = inputRef.current as HTMLInputElement; input.focus(); input.setSelectionRange(start + text.length, start + text.length); } }, 10); }; const handleFormatClick = (formatType: 'bold' | 'italic' | 'underline' | 'strike' | 'align-left' | 'align-center' | 'align-right' | 'clear' | 'size-up' | 'size-down' | 'color' | 'emoji', val?: string) => { if (formatType === 'emoji' && val) { insertAtCursor(val); return; } if (showFormatting) { document.execCommand('styleWithCSS', false, 'true'); if (formatType === 'bold') document.execCommand('bold', false); if (formatType === 'italic') document.execCommand('italic', false); if (formatType === 'underline') document.execCommand('underline', false); if (formatType === 'strike') document.execCommand('strikeThrough', false); if (formatType === 'align-left') document.execCommand('justifyLeft', false); if (formatType === 'align-center') document.execCommand('justifyCenter', false); if (formatType === 'align-right') document.execCommand('justifyRight', false); if (formatType === 'clear') document.execCommand('removeFormat', false); if (formatType === 'size-up') { const selection = window.getSelection(); if (selection && selection.rangeCount > 0) { const range = selection.getRangeAt(0); const span = document.createElement('span'); span.style.fontSize = '1.2em'; range.surroundContents(span); } } if (formatType === 'size-down') { const selection = window.getSelection(); if (selection && selection.rangeCount > 0) { const range = selection.getRangeAt(0); const span = document.createElement('span'); span.style.fontSize = '0.8em'; range.surroundContents(span); } } if (inputRef.current) { onChange(inputRef.current.innerHTML); } return; } if (onFormat) { onFormat(formatType === 'emoji' ? 'color' : formatType, val); return; } // Default basic formatting logic for text inputs (when showFormatting is false but we have basic logic) const input = inputRef.current as HTMLInputElement; if (!input) return; const start = input.selectionStart || 0; const end = input.selectionEnd || 0; const selectedText = value.substring(start, end); let newValue = value; if (formatType === 'bold') { newValue = value.substring(0, start) + `${selectedText}` + value.substring(end); } else if (formatType === 'italic') { newValue = value.substring(0, start) + `${selectedText}` + value.substring(end); } if (newValue !== value) { onChange(newValue); setTimeout(() => { if (inputRef.current) { const input = inputRef.current as HTMLInputElement; input.focus(); input.setSelectionRange(start, start + newValue.length - value.length + selectedText.length); } }, 10); } }; return (
{!labelHidden && ( )}
{Icon && (
)} {(showFormatting || showEmoji) ? ( } value={value} onChange={onChange} onBlur={onBlur} placeholder={placeholder} hasIcon={!!Icon} className={cn( "flex-1 border-slate-200 bg-slate-50/30 focus:bg-white focus:border-[#004449] transition-all rounded-[5px] shadow-none text-[13px] border h-10 min-h-[40px]", Icon ? (isRTL ? '!pr-12' : '!pl-12') : (isRTL ? 'pr-3.5' : 'pl-3.5'), (showEmoji || showFormatting || button) ? (isRTL ? '!pl-20' : '!pr-20') : (isRTL ? 'pr-3.5' : 'pl-3.5'), className )} /> ) : ( } type={type} value={value} onChange={(e) => onChange(e.target.value)} onBlur={onBlur} disabled={disabled} className={cn( "h-10 border-slate-200 bg-slate-50/30 focus:bg-white focus:border-[#004449] transition-all rounded-[5px] shadow-none text-[13px]", Icon ? (isRTL ? '!pr-12' : '!pl-12') : (isRTL ? 'pr-3.5' : 'pl-3.5'), (showEmoji || showFormatting || button) ? (isRTL ? '!pl-20' : '!pr-20') : (isRTL ? 'pr-3.5' : 'pl-3.5'), disabled && "opacity-50 cursor-not-allowed", className )} placeholder={placeholder} /> )}
{showFormatting && (
Formatting
)} {showEmoji && ( handleFormatClick('emoji', emojiData.emoji)} autoFocusSearch={true} theme={Theme.LIGHT} emojiStyle={EmojiStyle.APPLE} searchPlaceholder="Search emojis..." width={300} height={350} skinTonesDisabled={false} previewConfig={{ showPreview: false }} /> )} {button}
{description &&

}
); }; export const SettingPhoneInput: React.FC<{ label: string; value: string; onChange: (val: string) => void; description?: string; className?: string; defaultCountry?: import('libphonenumber-js').CountryCode; }> = ({ label, value, onChange, description, className = "", defaultCountry }) => (
{description &&

}
); export const MethodButton: React.FC<{ active: boolean; label: string; description?: string; icon?: React.ComponentType<{ size?: number; className?: string; strokeWidth?: number }>; iconPath?: string; onClick: () => void; color?: string; bg?: string; activeStyles?: string; disabled?: boolean; className?: string; }> = ({ active, label, description, icon: Icon, iconPath, onClick, color = "text-indigo-600", bg = "bg-indigo-50", activeStyles = "bg-white border-[#004449] ring-4 ring-[#004449]/5", disabled = false, className = "" }) => { const isRTL = typeof document !== 'undefined' && document.dir === 'rtl'; return (
); }; export const ColorInput: React.FC<{ label: string; value: string; onChange: (val: string) => void; }> = ({ label, value, onChange }) => (
onChange(e.target.value)} className="h-7 w-7 shrink-0 rounded border-0 overflow-hidden cursor-pointer shadow-none" /> onChange(e.target.value)} className="w-full text-[11px] font-mono font-bold uppercase outline-none text-slate-600" />
); export const CodeEditor: React.FC<{ value: string; onChange: (val: string) => void; label?: string; placeholder?: string; language?: string; }> = ({ value = "", onChange, label = "Custom CSS", placeholder = ".class { \n color: red; \n}", language = "css" }) => { const lineCount = (value || "").split('\n').length; const isEditing = value.trim().length > 0; return (
{language}
{/* Line Numbers Sidebar */}
{Array.from({ length: Math.max(lineCount, 15) }).map((_, i) => ( {i + 1} ))}
{ const p = window.Prism; if (!p || !p.languages.css) return code; return p.highlight(code, p.languages.css, 'css'); }} padding={16} placeholder={placeholder} className="font-mono text-[13px] leading-[21px] min-h-[350px] outline-none text-slate-300" style={{ fontFamily: '"Fira Code", "Fira Mono", monospace', backgroundColor: 'transparent', counterReset: 'line', }} />
{lineCount} lines
{isEditing ? : }

{isEditing ? 'Custom Styles Active' : 'Standard design is active'}

{isEditing ? 'Your custom styles are being applied with high priority to the registration form.' : 'Your custom styles will be injected into the footer and applied with high priority to override default layouts.' }

); }; export const IntegrationCard: React.FC<{ title: string; description: string; code: string; badge: string; badgeColor?: 'blue' | 'brand' | 'emerald'; icon: React.ComponentType<{ size?: number; className?: string; opacity?: number; shrink?: number | string }>; variant?: 'light' | 'brand'; }> = ({ title, description, code, badge, badgeColor = 'blue', icon: Icon }) => { const badgeStyles = { blue: 'bg-blue-50 text-blue-600 border-blue-100', brand: 'bg-[#004449]/10 text-[#004449] border-[#004449]/20', emerald: 'bg-emerald-50 text-emerald-600 border-emerald-100' }; return ( {badge}
} />
{code}
); }; export const ShortcodeManager: React.FC<{ shortcodes: string[]; onChange: (newShortcodes: string[]) => void; title: string; description: string; placeholder?: string; systemShortcode?: string; }> = ({ shortcodes, onChange, title, description, placeholder = "[your_shortcode]", systemShortcode }) => { const add = () => onChange([...shortcodes, ""]); const remove = (index: number) => onChange(shortcodes.filter((_, i) => i !== index)); const update = (index: number, val: string) => { const next = [...shortcodes]; next[index] = val; onChange(next); }; return ( Add Extension } />
{systemShortcode && (
{systemShortcode}

Primary Registration Shortcode

Default
)} {shortcodes.map((sc, idx) => (
update(idx, e.target.value)} className="w-full pl-12 pr-14 h-11 bg-transparent border-0 focus:ring-0 outline-none font-mono text-[13px] font-bold text-slate-700" placeholder={placeholder} />
))}
{shortcodes.length === 0 && !systemShortcode && (
No extensions defined

Use custom shortcodes to inject content before or after the form.

)}
); }; export const BannerCard: React.FC<{ title: string; description?: React.ReactNode; badge: string; color: 'emerald' | 'blue' | 'orange'; icon?: React.ComponentType<{ size?: number; className?: string; opacity?: number; shrink?: number | string; strokeWidth?: number }>; iconPath?: string; children?: React.ReactNode; }> = ({ title, description, badge, color, icon: Icon, iconPath, children }) => { const styles = { emerald: { badgeText: 'text-emerald-600 border-emerald-200 bg-emerald-50' }, blue: { badgeText: 'text-blue-600 border-blue-200 bg-blue-50' }, orange: { badgeText: 'text-orange-600 border-orange-200 bg-orange-50' } }; const s = styles[color]; return ( {badge} } />
{children}
); }; export const FlatTabs: React.FC<{ tabs: { id: string; label: string; icon: React.ComponentType<{ size?: number; className?: string }> }[]; activeTab: string; onTabChange: (id: string) => void; className?: string; }> = ({ tabs, activeTab, onTabChange, className = "" }) => (
{tabs.map(tab => ( ))}
); export { Tabs, TabsList, TabsTrigger, TabsContent } from './tabs'; export const PageHeader: React.FC<{ title: React.ReactNode; description: React.ReactNode; }> = ({ title, description }) => (

); export const SettingsLayout: React.FC<{ children: React.ReactNode; className?: string; style?: React.CSSProperties; id?: string; dir?: 'ltr' | 'rtl'; maxWidth?: string; noPadding?: boolean; }> = ({ children, className = "", style, id, dir, maxWidth = "max-w-7xl", noPadding = false }) => (
{children}
); export const SettingsHeader: React.FC<{ title: string; description: string; backUrl?: string; onBack?: () => void; children?: React.ReactNode; className?: string; }> = ({ title, description, backUrl, onBack, children, className = "" }) => (
{onBack ? ( ) : backUrl ? ( ) : null}
{onBack ? ( ) : backUrl ? ( ) : null} {children}
); // --- Standardized Button Components (from User Snippets) --- export const AdminButton: React.FC<{ children?: React.ReactNode; onClick?: (e?: React.MouseEvent) => void; disabled?: boolean; className?: string; icon?: React.ComponentType<{ size?: number; className?: string; strokeWidth?: number }>; loading?: boolean; size?: 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm' | 'default'; asChild?: boolean; type?: 'button' | 'submit' | 'reset'; title?: string; }> = ({ children, onClick, disabled, className = "", icon: Icon, loading, size = 'default', asChild = false, ...props }) => { const Comp = (asChild ? Slot : "button") as React.ElementType; const sizeClasses = { sm: 'h-8 px-3 text-[12px]', md: 'h-9 px-4 text-[13px]', lg: 'h-10 px-6 text-[14px]', icon: 'w-9 h-9 p-0', 'icon-sm': 'w-8 h-8 p-0', default: 'h-8 px-3 text-[12px]' }; return ( {!asChild && (loading ?
: Icon && )} {children} ); }; export const SecondaryButton: React.FC<{ children?: React.ReactNode; onClick?: (e?: React.MouseEvent) => void; disabled?: boolean; className?: string; icon?: React.ComponentType<{ size?: number; className?: string; strokeWidth?: number }>; loading?: boolean; size?: 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm' | 'default'; asChild?: boolean; type?: 'button' | 'submit' | 'reset'; title?: string; }> = ({ children, onClick, disabled, className = "", icon: Icon, loading, size = 'default', asChild = false, ...props }) => { const Comp = (asChild ? Slot : "button") as React.ElementType; const sizeClasses = { sm: 'h-8 px-3 text-[12px]', md: 'h-9 px-4 text-[13px]', lg: 'h-10 px-6 text-[14px]', icon: 'w-9 h-9 p-0', 'icon-sm': 'w-8 h-8 p-0', default: 'h-8 px-3 text-[12px]' }; return ( {!asChild && (loading ?
: Icon && )} {children} ); }; export const GhostButton: React.FC<{ children?: React.ReactNode; onClick?: (e?: React.MouseEvent) => void; disabled?: boolean; className?: string; icon?: React.ComponentType<{ size?: number; className?: string; strokeWidth?: number }>; loading?: boolean; size?: 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm' | 'default'; asChild?: boolean; type?: 'button' | 'submit' | 'reset'; title?: string; }> = ({ children, onClick, disabled, className = "", icon: Icon, loading, size = 'default', asChild = false, ...props }) => { const Comp = (asChild ? Slot : "button") as React.ElementType; const sizeClasses = { sm: 'h-8 px-3 text-[12px]', md: 'h-9 px-4 text-[13px]', lg: 'h-10 px-6 text-[14px]', icon: 'w-9 h-9 p-0', 'icon-sm': 'w-8 h-8 p-0', default: 'h-8 px-3 text-[12px]' }; return ( {!asChild && (loading ?
: Icon && )} {children} ); }; export const SuccessButton: React.FC<{ children: React.ReactNode; onClick?: (e?: React.MouseEvent) => void; disabled?: boolean; className?: string; icon?: React.ComponentType<{ size?: number; className?: string; strokeWidth?: number }>; loading?: boolean; size?: 'sm' | 'md' | 'lg' | 'default'; }> = ({ children, onClick, disabled, className = "", icon: Icon, loading, size = 'default' }) => { const sizeClasses = { sm: 'h-8 px-3 text-[12px]', md: 'h-9 px-4 text-[13px]', lg: 'h-10 px-6 text-[14px]', icon: 'w-9 h-9 p-0', 'icon-sm': 'w-8 h-8 p-0', default: 'h-8 px-3 text-[12px]' }; return ( ); }; // Aliases for compatibility export const PrimaryButton = AdminButton; export const OutlineButton = SecondaryButton; export const SettingCard: React.FC<{ children: React.ReactNode; className?: string; dir?: 'ltr' | 'rtl'; }> = ({ children, className = "", dir }) => (
{children}
); export const SnapshotStat: React.FC<{ value: string; label: string; sub: string; badge: string; badgeVariant?: 'secondary' | 'green' | 'yellow' | 'blue' | 'gray' | 'success' | 'warning' | 'outline'; percent: string | number; barColor: string; valueClass?: string; percentText?: string; icon?: React.ComponentType<{ className?: string }>; }> = ({ value, label, sub, badge, badgeVariant = 'secondary', percent, barColor, valueClass = 'text-gray-900', percentText, icon: Icon }) => { const cleanPct = (percent?.toString() || "0").replace(/%/g, ""); const displayPercent = percentText || `${cleanPct}%`; const variantMap: Record = { success: 'green', warning: 'yellow', }; const finalVariant = variantMap[badgeVariant] || badgeVariant; return (
['variant']} className="text-[10px] px-1.5 py-0 h-4.5 font-bold uppercase tracking-wider">

{Icon && } [{displayPercent}]

); }; export const FeatSection: React.FC<{ title: string; description?: string; children: React.ReactNode; badge?: string; icon?: React.ComponentType<{ className?: string; opacity?: number; shrink?: number | string }>; className?: string; gridCols?: string; }> = ({ title, description, children, badge, icon: Icon, className = "", gridCols = "grid-cols-1 lg:grid-cols-2" }) => (
{badge} )} />
{children}
); export const FeatRow: React.FC<{ icon: React.ComponentType<{ className?: string; opacity?: number; shrink?: number | string }>; title: string; desc: string; enabled: boolean; allowed: boolean; error?: string; onToggle: () => void; docs?: string; settingsUrl?: string; upgradeUrl?: string; pluginLink?: string; t?: { labelPro?: string; btnSettings?: string; btnUpgrade?: string; btnActivate?: string; }; }> = ({ icon: Icon, title, desc, enabled, allowed, error, onToggle, docs, settingsUrl, upgradeUrl, pluginLink, t = {} }) => { return (
{Icon && }
{!allowed && ( )} {error && !(allowed && pluginLink) && ( )}

{!allowed && upgradeUrl ? ( window.open(upgradeUrl, '_blank')} > ) : allowed && error && pluginLink ? ( window.open(pluginLink, '_blank')} > ) : (
)}
{enabled && allowed && (
Active
)} {!enabled && allowed && !error && (
Inert
)}
{enabled && allowed && settingsUrl && ( )} {docs && allowed && ( )}
); }; export const ShortcodeGrid: React.FC<{ title: string; description: string; shortcodes: { title: string; description: string; code: string; icon?: React.ComponentType<{ size?: number; className?: string; strokeWidth?: number }> }[]; }> = ({ title, description, shortcodes }) => { const handleCopy = (code: string) => { navigator.clipboard.writeText(code).then(() => { toast.success("Shortcode copied to clipboard!", { description: "You can now paste it into any page or theme builder block." }); }); }; return (
{shortcodes.map((s, idx) => (
{s.icon && (
)}

{s.title}

{s.description}

{s.code}
))}
); }; export const HeaderSaveStatus: React.FC<{ status: 'idle' | 'saving' | 'saved' | 'error' | 'unsaved'; }> = ({ status }) => (
{status === 'saving' && (
Saving Changes... )} {status === 'saved' && ( Changes Saved )} {status === 'unsaved' && (
Unsaved Changes )} {status === 'error' && ( Save Failed )}
); export const ModernCardHeader: React.FC<{ title: React.ReactNode; description?: React.ReactNode; icon?: React.ElementType; iconPath?: string; rightAction?: React.ReactNode; className?: string; }> = ({ title, description, icon: Icon, iconPath, rightAction, className = "" }) => (
{iconPath ? ( ) : Icon && }
{description && (
)}
{rightAction &&
{rightAction}
}
); export const SettingsTabContent: React.FC<{ id: string; activeTab: string; children: React.ReactNode; }> = ({ id, activeTab, children }) => { return ( {id === activeTab && ( {children} )} ); }; export const SettingsGrid: React.FC<{ children: React.ReactNode; cols?: 1 | 2 | 3; gap?: number; className?: string; }> = ({ children, cols = 2, gap = 6, className }) => { const gridCols = { 1: 'grid-cols-1', 2: 'grid-cols-1 lg:grid-cols-2', 3: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3', }[cols]; return (
{children}
); }; export const SaveWithStatus: React.FC<{ saveStatus: 'idle' | 'saving' | 'saved' | 'error'; hasUnsavedChanges: boolean; isSaving: boolean; onSave: () => void; t?: { saving?: string; synchronized?: string; unsaved?: string; saveChanges?: string; }; }> = ({ saveStatus, hasUnsavedChanges, isSaving, onSave, t = {} }) => { return (
{saveStatus === 'saving' && ( {t.saving || 'Saving...'} )} {saveStatus === 'saved' && ( {t.synchronized || 'Synchronized'} )} {hasUnsavedChanges && saveStatus === 'idle' && ( {t.unsaved || 'Unsaved Changes'} )} {saveStatus === 'error' && ( Error Saving )} {isSaving ? (t.saving || 'Saving...') : (t.saveChanges || 'Save Changes')}
); };