77 lines
2.8 KiB
TypeScript
77 lines
2.8 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect, useRef } from 'react';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import { Loader2, Check } from 'lucide-react';
|
|
import { saveUserNote } from '@/lib/actions/user-tabs';
|
|
import { toast } from 'sonner';
|
|
|
|
interface AdminNoteProps {
|
|
userId: string;
|
|
initialNote?: string;
|
|
}
|
|
|
|
export function AdminNote({ userId, initialNote = '' }: AdminNoteProps) {
|
|
const [content, setContent] = useState(initialNote);
|
|
const [status, setStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
|
|
const timeoutRef = useRef<NodeJS.Timeout>(null);
|
|
|
|
// Sync remote changes if any (unlikely in single user flow but good practice)
|
|
useEffect(() => {
|
|
setContent(initialNote || '');
|
|
}, [initialNote]);
|
|
|
|
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
const newValue = e.target.value;
|
|
setContent(newValue);
|
|
setStatus('idle');
|
|
|
|
// Debounce save
|
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
|
|
timeoutRef.current = setTimeout(async () => {
|
|
if (newValue.trim() === initialNote.trim()) return;
|
|
|
|
setStatus('saving');
|
|
const result = await saveUserNote(userId, newValue);
|
|
|
|
if (result.success) {
|
|
setStatus('saved');
|
|
// Reset saved status after 2 seconds
|
|
setTimeout(() => setStatus('idle'), 2000);
|
|
} else {
|
|
setStatus('error');
|
|
toast.error("Failed to save note");
|
|
}
|
|
}, 1000); // 1s delay
|
|
};
|
|
|
|
return (
|
|
<div className="relative">
|
|
<Textarea
|
|
value={content}
|
|
onChange={handleChange}
|
|
placeholder="Add private notes about this user..."
|
|
className="min-h-[120px] bg-amber-50/50 border-amber-200/50 resize-y text-sm focus-visible:ring-amber-500/20"
|
|
/>
|
|
<div className="absolute bottom-2 right-2 flex items-center gap-1.5 pointer-events-none">
|
|
{status === 'saving' && (
|
|
<span className="text-[10px] text-amber-600 flex items-center gap-1 bg-white/50 px-1.5 rounded-full">
|
|
<Loader2 className="h-3 w-3 animate-spin" /> Saving...
|
|
</span>
|
|
)}
|
|
{status === 'saved' && (
|
|
<span className="text-[10px] text-emerald-600 flex items-center gap-1 bg-white/50 px-1.5 rounded-full font-medium">
|
|
<Check className="h-3 w-3" /> Saved
|
|
</span>
|
|
)}
|
|
{status === 'error' && (
|
|
<span className="text-[10px] text-red-600 bg-white/50 px-1.5 rounded-full">
|
|
Not Saved
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|