import { useState, useEffect, useRef } from 'react';
import {
  Sparkles, Save, Trash2, Lightbulb,
  CheckSquare, FileText, FolderKanban, Clock,
  User, Calendar, ArrowRight, Mic, MicOff,
  Paperclip, X, FileImage, FileCode, File
} from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import { CATEGORIES, PRIORITIES, getCategoryClass, getPriorityClass } from '../utils';

const HISTORY_KEY = 'quicknote_history';
function loadHistory() { try { return JSON.parse(localStorage.getItem(HISTORY_KEY) || '[]'); } catch { return []; } }
function pushHistory(item) {
  const h = loadHistory();
  h.unshift({ ...item, savedAt: new Date().toISOString() });
  localStorage.setItem(HISTORY_KEY, JSON.stringify(h.slice(0, 8)));
}

const TYPE_CONFIG = {
  task:    { icon: CheckSquare,  color: 'text-primary',    bg: 'bg-primary/10',                        label: 'Task'    },
  note:    { icon: FileText,     color: 'text-teal-600',   bg: 'bg-teal-50 dark:bg-teal-900/20',       label: 'Note'    },
  project: { icon: FolderKanban, color: 'text-violet-600', bg: 'bg-violet-50 dark:bg-violet-900/20',   label: 'Project' },
};

const EXAMPLES = [
  'Follow up with Hamid about WEF Lighthouse document, urgent by end of week',
  'Benchmark presentation meeting with Shabeer on 13 Apr, high priority',
  'Note: Google NDA meeting happened — next step is Legal Affairs review',
  'New project: AI Ethics workshop for Champions Program, strategic initiative',
];

function fileIcon(mime) {
  if (!mime) return File;
  if (mime.startsWith('image/')) return FileImage;
  if (mime === 'application/pdf') return FileCode;
  return File;
}

// ── Parsed result card ───────────────────────────────────────────────────────
function ParsedCard({ data, onChange, onSave, onDiscard }) {
  const TypeCfg = TYPE_CONFIG[data.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;

  return (

        <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center`}>
          <TypeIcon size={15} className={TypeCfg.color} />

AI Parsed Result

Review and edit before saving

        <select
          value={data.type}
          onChange={e => onChange('type', e.target.value)}
          className="text-xs font-semibold border border-primary/30 rounded-lg px-2.5 py-1.5 bg-white dark:bg-gray-800 text-primary focus:outline-none focus:ring-2 focus:ring-primary/30"
        >
          <option value="task">Task</option>
          <option value="note">Note</option>
          <option value="project">Project</option>
        </select>

          Title
          <input className="input font-semibold text-base" value={data.title} onChange={e => onChange('title', e.target.value)} placeholder="Title…" />

            Category
            <select className="select text-sm" value={data.category} onChange={e => onChange('category', e.target.value)}>
              {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </select>

            Priority
            <select className="select text-sm" value={data.priority} onChange={e => onChange('priority', e.target.value)}>
              {PRIORITIES.map(p => <option key={p}>{p}</option>)}
            </select>

        {data.type === 'task' && (

              <User size={10} /> Assignee
              <input className="input text-sm" value={data.assignee || ''} onChange={e => onChange('assignee', e.target.value)} placeholder="Person name" />

              <Calendar size={10} /> Due Date
              <input type="date" className="input text-sm" value={data.due || ''} onChange={e => onChange('due', e.target.value)} />

        )}

          Details / Notes
          <textarea className="input resize-none text-sm" rows={3} value={data.details || ''} onChange={e => onChange('details', e.target.value)} placeholder="Context…" />

        {data.type === 'task' && (

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.isFollowUp} onChange={e => onChange('isFollowUp', e.target.checked)} />
              Follow-up needed

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.suggestedTodayFocus} onChange={e => onChange('suggestedTodayFocus', e.target.checked)} />
              Pin to today

        )}
        {data.projectHint && (

            <Lightbulb size={13} className="text-amber-600 shrink-0" />

              Suggested project: {data.projectHint}

        )}

        <button onClick={onDiscard} className="btn-ghost flex items-center gap-1.5 text-sm text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20">
          <Trash2 size={13} /> Discard
        </button>

        <button onClick={onSave} className="btn-primary flex items-center gap-2">
          <Save size={14} /> Save to Workspace
        </button>

  );
}

// ── History card ─────────────────────────────────────────────────────────────
function HistoryItem({ item }) {
  const TypeCfg = TYPE_CONFIG[item.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;
  const when = new Date(item.savedAt);
  const timeStr = when.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });

  return (

      <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center shrink-0`}>
        <TypeIcon size={14} className={TypeCfg.color} />

{item.title}

          {item.category && (
            <span className={`badge text-[10px] py-0 ${getCategoryClass(item.category)}`}>
              {CATEGORIES[item.category]?.label?.split(' ')[0]}

          )}
          {item.priority && (
            <span className={`badge text-[10px] py-0 ${getPriorityClass(item.priority)}`}>{item.priority}
          )}
          {timeStr}

  );
}

// ── Main component ────────────────────────────────────────────────────────────
export default function QuickNote() {
  const [text, setText]             = useState('');
  const [processing, setProcessing] = useState(false);
  const [editData, setEditData]     = useState(null);
  const [projects, setProjects]     = useState([]);
  const [history, setHistory]       = useState(loadHistory);
  const [showExamples, setShowEx]   = useState(false); // unused, kept for safe removal
  const [listening, setListening]   = useState(false);
  const [attachedFile, setAttached] = useState(null); // { name, mimeType, dataUrl }
  const textareaRef  = useRef(null);
  const fileInputRef = useRef(null);
  const recognitionRef = useRef(null);

  useEffect(() => { api.get('/projects').then(setProjects).catch(() => {}); }, []);

  // ── Voice ────────────────────────────────────────────────────────────────
  const startVoice = () => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) { toast.error('Voice not supported in this browser'); return; }
    const rec = new SR();
    rec.continuous = true;
    rec.interimResults = true;
    rec.lang = 'en-US';
    let base = text;
    rec.onresult = (e) => {
      let interim = '';
      let final = '';
      for (let i = e.resultIndex; i < e.results.length; i++) {
        if (e.results[i].isFinal) final += e.results[i][0].transcript;
        else interim += e.results[i][0].transcript;
      }
      if (final) base = base + (base ? ' ' : '') + final;
      setText(base + (interim ? ' ' + interim : ''));
    };
    rec.onerror = () => { setListening(false); };
    rec.onend   = () => { setListening(false); };
    recognitionRef.current = rec;
    rec.start();
    setListening(true);
  };

  const stopVoice = () => {
    recognitionRef.current?.stop();
    setListening(false);
  };

  // ── File attachment ──────────────────────────────────────────────────────
  const handleFile = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      setAttached({ name: file.name, mimeType: file.type, dataUrl: ev.target.result });
      toast.success(`Attached: ${file.name}`);
    };
    reader.readAsDataURL(file);
    e.target.value = '';
  };

  const removeAttachment = () => setAttached(null);

  // ── AI processing ────────────────────────────────────────────────────────
  const process = async () => {
    if (!text.trim() && !attachedFile) { toast.error('Type something or attach a file first'); return; }
    setProcessing(true);
    try {
      let result;
      if (attachedFile) {
        result = await api.post('/ai/parse-file', {
          fileData: attachedFile.dataUrl,
          mimeType: attachedFile.mimeType,
          fileName: attachedFile.name,
          text: text.trim(),
        });
      } else {
        result = await api.post('/ai/parse', { text });
      }
      setEditData({ ...result });
    } catch (e) {
      toast.error('AI parsing failed: ' + e.message);
    } finally { setProcessing(false); }
  };

  const changeField = (k, v) => setEditData(d => ({ ...d, [k]: v }));

  // ── Save ─────────────────────────────────────────────────────────────────
  const save = async () => {
    if (!editData) return;
    try {
      const d = editData;
      if (d.type === 'task') {
        await api.post('/tasks', {
          title: d.title, notes: d.details, category: d.category, priority: d.priority,
          assignee: d.assignee || '', due_date: d.due || '',
          is_follow_up: d.isFollowUp ? 1 : 0,
          is_today_focus: d.suggestedTodayFocus ? 1 : 0,
        });
        toast.success('Task saved!');
      } else if (d.type === 'note') {
        await api.post('/notes', { title: d.title, body: d.details });
        toast.success('Note saved!');
      } else {
        await api.post('/projects', { name: d.title, category: d.category, description: d.details, priority: d.priority });
        toast.success('Project created!');
      }
      pushHistory({ type: d.type, title: d.title, category: d.category, priority: d.priority });
      setHistory(loadHistory());
      setEditData(null); setText(''); setAttached(null);
      textareaRef.current?.focus();
    } catch (e) { toast.error(e.message); }
  };

  const discard = () => setEditData(null);
  const useExample = (ex) => { setText(ex); setShowEx(false); textareaRef.current?.focus(); };

  const AttachIcon = attachedFile ? fileIcon(attachedFile.mimeType) : Paperclip;

  return (

      {/* Header — vibrant gradient */}

            <Sparkles size={15} className="text-white" />

Quick Note

Type, speak, or attach — AI structures it instantly

      {/* Main content */}

        {/* ── LEFT: Input + Parsed result ────────────────────────────── */}

          {/* Input card — gradient border accent */}

            {/* Top accent bar */}

            {/* Textarea — 3 rows on mobile, 5 on desktop */}
            <textarea
              ref={textareaRef}
              value={text}
              onChange={e => setText(e.target.value)}
              onKeyDown={e => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') process(); }}
              rows={3}
              placeholder={"Type anything — task, idea, follow-up, meeting note…"}
              className="w-full px-4 pt-4 pb-2 text-sm sm:text-base bg-transparent border-none outline-none resize-none text-gray-900 dark:text-white placeholder-gray-300 dark:placeholder-gray-600 font-medium leading-relaxed sm:rows-5"
              style={{ minHeight: '80px' }}
            />

            {/* Attached file pill */}
            {attachedFile && (

                <AttachIcon size={12} className="text-violet-600 shrink-0" />
                {attachedFile.name}
                <button onClick={removeAttachment} className="text-violet-400 hover:text-red-500 transition-colors">
                  <X size={12} />
                </button>

            )}

            {/* Toolbar */}

              {/* Examples */}
              <button
                onClick={() => setShowEx(!showExamples)}
                className="flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
              >
                <Lightbulb size={12} /> Examples
              </button>

              {/* Voice button */}
              <button
                onClick={listening ? stopVoice : startVoice}
                title={listening ? 'Stop recording' : 'Start voice input'}
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  listening
                    ? 'bg-red-500 text-white animate-pulse shadow-md'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                {listening ? <MicOff size={12} /> : <Mic size={12} />}
                {listening ? 'Stop' : 'Voice'}
                {listening ? 'Stop' : ''}
              </button>

              {/* Attach file button */}
              <button
                onClick={() => fileInputRef.current?.click()}
                title="Attach file — image, PDF, or text"
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  attachedFile
                    ? 'bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                <Paperclip size={12} />
                {attachedFile ? 'Change' : 'Attach'}
              </button>
              <input
                ref={fileInputRef}
                type="file"
                className="hidden"
                accept="image/*,.pdf,.txt,.csv,.md"
                onChange={handleFile}
              />

              {(text.trim() || attachedFile) && !processing && !editData && (
                <button onClick={() => { setText(''); setAttached(null); discard(); }} className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 px-2 transition-colors">
                  Clear
                </button>
              )}

              <button
                onClick={process}
                disabled={(!text.trim() && !attachedFile) || processing || !!editData}
                className="btn-primary flex items-center gap-1.5 py-1.5 px-4 text-xs sm:text-sm disabled:opacity-40 bg-gradient-to-r from-violet-600 to-primary border-0"
              >
                {processing ? (
                  <> Parsing…</>
                ) : (
                  <><Sparkles size={13} /> Process</>
                )}
              </button>

            {/* Example suggestions */}
            {showExamples && (

Try these

                {EXAMPLES.map((ex, i) => (
                  <button key={i} onClick={() => useExample(ex)}
                    className="flex items-center gap-2 w-full text-left px-3 py-2 rounded-xl text-xs text-gray-600 dark:text-gray-400 hover:bg-white dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white transition-colors group">
                    <ArrowRight size={11} className="shrink-0 text-gray-400 group-hover:text-primary transition-colors" />
                    {ex}
                  </button>
                ))}

            )}

          {/* Parsed result */}
          {editData && (
            <ParsedCard
              data={editData}
              onChange={changeField}
              projects={projects}
              onSave={save}
              onDiscard={discard}
            />
          )}

          {/* Empty state */}
          {!editData && !processing && (

              <Sparkles size={32} className="text-gray-300 dark:text-gray-700 mb-3" />

Type, speak, or attach a file — then click Process

Claude will parse it into a task, note, or project

          )}

        {/* ── RIGHT: History ──────────────────────────────────────────── */}

Recent Captures

Last {history.length} items saved via Quick Note

          {history.length === 0 ? (

              <Clock size={28} className="text-gray-200 dark:text-gray-700 mb-3" />

Nothing captured yet

Your saved items will appear here

          ) : (

              {history.map((item, i) => <HistoryItem key={i} item={item} />)}

          )}

          {/* Tip box */}

Tips

• ⌘ Enter to process instantly

• Click Voice to dictate hands-free

• Attach a PDF, image, or text — AI reads it

  );
}

-  'Follow up with Hamid about WEF Lighthouse document, urgent by end of week',

-  'Benchmark presentation meeting with Shabeer on 13 Apr, high priority',

-  'Note: Google NDA meeting happened — next step is Legal Affairs review',

-  'New project: AI Ethics workshop for Champions Program, strategic initiative',


Now remove the Examples button from the toolbar and the dropdown block, and clean up the unused state/function:


Edit

const [showExamples, setShowEx]   = useState(false); // unused, kept for safe removal


The file /Users/khalidbinkalban/my-workspace/client/src/components/Inbox.jsx has been updated successfully.

import { useState, useEffect, useRef } from 'react';
import {
  Sparkles, Save, Trash2, Lightbulb,
  CheckSquare, FileText, FolderKanban, Clock,
  User, Calendar, ArrowRight, Mic, MicOff,
  Paperclip, X, FileImage, FileCode, File
} from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import { CATEGORIES, PRIORITIES, getCategoryClass, getPriorityClass } from '../utils';

const HISTORY_KEY = 'quicknote_history';
function loadHistory() { try { return JSON.parse(localStorage.getItem(HISTORY_KEY) || '[]'); } catch { return []; } }
function pushHistory(item) {
  const h = loadHistory();
  h.unshift({ ...item, savedAt: new Date().toISOString() });
  localStorage.setItem(HISTORY_KEY, JSON.stringify(h.slice(0, 8)));
}

const TYPE_CONFIG = {
  task:    { icon: CheckSquare,  color: 'text-primary',    bg: 'bg-primary/10',                        label: 'Task'    },
  note:    { icon: FileText,     color: 'text-teal-600',   bg: 'bg-teal-50 dark:bg-teal-900/20',       label: 'Note'    },
  project: { icon: FolderKanban, color: 'text-violet-600', bg: 'bg-violet-50 dark:bg-violet-900/20',   label: 'Project' },
};

function fileIcon(mime) {
  if (!mime) return File;
  if (mime.startsWith('image/')) return FileImage;
  if (mime === 'application/pdf') return FileCode;
  return File;
}

// ── Parsed result card ───────────────────────────────────────────────────────
function ParsedCard({ data, onChange, onSave, onDiscard }) {
  const TypeCfg = TYPE_CONFIG[data.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;

  return (

        <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center`}>
          <TypeIcon size={15} className={TypeCfg.color} />

AI Parsed Result

Review and edit before saving

        <select
          value={data.type}
          onChange={e => onChange('type', e.target.value)}
          className="text-xs font-semibold border border-primary/30 rounded-lg px-2.5 py-1.5 bg-white dark:bg-gray-800 text-primary focus:outline-none focus:ring-2 focus:ring-primary/30"
        >
          <option value="task">Task</option>
          <option value="note">Note</option>
          <option value="project">Project</option>
        </select>

          Title
          <input className="input font-semibold text-base" value={data.title} onChange={e => onChange('title', e.target.value)} placeholder="Title…" />

            Category
            <select className="select text-sm" value={data.category} onChange={e => onChange('category', e.target.value)}>
              {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </select>

            Priority
            <select className="select text-sm" value={data.priority} onChange={e => onChange('priority', e.target.value)}>
              {PRIORITIES.map(p => <option key={p}>{p}</option>)}
            </select>

        {data.type === 'task' && (

              <User size={10} /> Assignee
              <input className="input text-sm" value={data.assignee || ''} onChange={e => onChange('assignee', e.target.value)} placeholder="Person name" />

              <Calendar size={10} /> Due Date
              <input type="date" className="input text-sm" value={data.due || ''} onChange={e => onChange('due', e.target.value)} />

        )}

          Details / Notes
          <textarea className="input resize-none text-sm" rows={3} value={data.details || ''} onChange={e => onChange('details', e.target.value)} placeholder="Context…" />

        {data.type === 'task' && (

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.isFollowUp} onChange={e => onChange('isFollowUp', e.target.checked)} />
              Follow-up needed

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.suggestedTodayFocus} onChange={e => onChange('suggestedTodayFocus', e.target.checked)} />
              Pin to today

        )}
        {data.projectHint && (

            <Lightbulb size={13} className="text-amber-600 shrink-0" />

              Suggested project: {data.projectHint}

        )}

        <button onClick={onDiscard} className="btn-ghost flex items-center gap-1.5 text-sm text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20">
          <Trash2 size={13} /> Discard
        </button>

        <button onClick={onSave} className="btn-primary flex items-center gap-2">
          <Save size={14} /> Save to Workspace
        </button>

  );
}

// ── History card ─────────────────────────────────────────────────────────────
function HistoryItem({ item }) {
  const TypeCfg = TYPE_CONFIG[item.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;
  const when = new Date(item.savedAt);
  const timeStr = when.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });

  return (

      <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center shrink-0`}>
        <TypeIcon size={14} className={TypeCfg.color} />

{item.title}

          {item.category && (
            <span className={`badge text-[10px] py-0 ${getCategoryClass(item.category)}`}>
              {CATEGORIES[item.category]?.label?.split(' ')[0]}

          )}
          {item.priority && (
            <span className={`badge text-[10px] py-0 ${getPriorityClass(item.priority)}`}>{item.priority}
          )}
          {timeStr}

  );
}

// ── Main component ────────────────────────────────────────────────────────────
export default function QuickNote() {
  const [text, setText]             = useState('');
  const [processing, setProcessing] = useState(false);
  const [editData, setEditData]     = useState(null);
  const [projects, setProjects]     = useState([]);
  const [history, setHistory]       = useState(loadHistory);
  const [showExamples, setShowEx]   = useState(false); // unused, kept for safe removal
  const [listening, setListening]   = useState(false);
  const [attachedFile, setAttached] = useState(null); // { name, mimeType, dataUrl }
  const textareaRef  = useRef(null);
  const fileInputRef = useRef(null);
  const recognitionRef = useRef(null);

  useEffect(() => { api.get('/projects').then(setProjects).catch(() => {}); }, []);

  // ── Voice ────────────────────────────────────────────────────────────────
  const startVoice = () => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) { toast.error('Voice not supported in this browser'); return; }
    const rec = new SR();
    rec.continuous = true;
    rec.interimResults = true;
    rec.lang = 'en-US';
    let base = text;
    rec.onresult = (e) => {
      let interim = '';
      let final = '';
      for (let i = e.resultIndex; i < e.results.length; i++) {
        if (e.results[i].isFinal) final += e.results[i][0].transcript;
        else interim += e.results[i][0].transcript;
      }
      if (final) base = base + (base ? ' ' : '') + final;
      setText(base + (interim ? ' ' + interim : ''));
    };
    rec.onerror = () => { setListening(false); };
    rec.onend   = () => { setListening(false); };
    recognitionRef.current = rec;
    rec.start();
    setListening(true);
  };

  const stopVoice = () => {
    recognitionRef.current?.stop();
    setListening(false);
  };

  // ── File attachment ──────────────────────────────────────────────────────
  const handleFile = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      setAttached({ name: file.name, mimeType: file.type, dataUrl: ev.target.result });
      toast.success(`Attached: ${file.name}`);
    };
    reader.readAsDataURL(file);
    e.target.value = '';
  };

  const removeAttachment = () => setAttached(null);

  // ── AI processing ────────────────────────────────────────────────────────
  const process = async () => {
    if (!text.trim() && !attachedFile) { toast.error('Type something or attach a file first'); return; }
    setProcessing(true);
    try {
      let result;
      if (attachedFile) {
        result = await api.post('/ai/parse-file', {
          fileData: attachedFile.dataUrl,
          mimeType: attachedFile.mimeType,
          fileName: attachedFile.name,
          text: text.trim(),
        });
      } else {
        result = await api.post('/ai/parse', { text });
      }
      setEditData({ ...result });
    } catch (e) {
      toast.error('AI parsing failed: ' + e.message);
    } finally { setProcessing(false); }
  };

  const changeField = (k, v) => setEditData(d => ({ ...d, [k]: v }));

  // ── Save ─────────────────────────────────────────────────────────────────
  const save = async () => {
    if (!editData) return;
    try {
      const d = editData;
      if (d.type === 'task') {
        await api.post('/tasks', {
          title: d.title, notes: d.details, category: d.category, priority: d.priority,
          assignee: d.assignee || '', due_date: d.due || '',
          is_follow_up: d.isFollowUp ? 1 : 0,
          is_today_focus: d.suggestedTodayFocus ? 1 : 0,
        });
        toast.success('Task saved!');
      } else if (d.type === 'note') {
        await api.post('/notes', { title: d.title, body: d.details });
        toast.success('Note saved!');
      } else {
        await api.post('/projects', { name: d.title, category: d.category, description: d.details, priority: d.priority });
        toast.success('Project created!');
      }
      pushHistory({ type: d.type, title: d.title, category: d.category, priority: d.priority });
      setHistory(loadHistory());
      setEditData(null); setText(''); setAttached(null);
      textareaRef.current?.focus();
    } catch (e) { toast.error(e.message); }
  };

  const discard = () => setEditData(null);
  const useExample = (ex) => { setText(ex); setShowEx(false); textareaRef.current?.focus(); };

  const AttachIcon = attachedFile ? fileIcon(attachedFile.mimeType) : Paperclip;

  return (

      {/* Header — vibrant gradient */}

            <Sparkles size={15} className="text-white" />

Quick Note

Type, speak, or attach — AI structures it instantly

      {/* Main content */}

        {/* ── LEFT: Input + Parsed result ────────────────────────────── */}

          {/* Input card — gradient border accent */}

            {/* Top accent bar */}

            {/* Textarea — 3 rows on mobile, 5 on desktop */}
            <textarea
              ref={textareaRef}
              value={text}
              onChange={e => setText(e.target.value)}
              onKeyDown={e => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') process(); }}
              rows={3}
              placeholder={"Type anything — task, idea, follow-up, meeting note…"}
              className="w-full px-4 pt-4 pb-2 text-sm sm:text-base bg-transparent border-none outline-none resize-none text-gray-900 dark:text-white placeholder-gray-300 dark:placeholder-gray-600 font-medium leading-relaxed sm:rows-5"
              style={{ minHeight: '80px' }}
            />

            {/* Attached file pill */}
            {attachedFile && (

                <AttachIcon size={12} className="text-violet-600 shrink-0" />
                {attachedFile.name}
                <button onClick={removeAttachment} className="text-violet-400 hover:text-red-500 transition-colors">
                  <X size={12} />
                </button>

            )}

            {/* Toolbar */}

              {/* Examples */}
              <button
                onClick={() => setShowEx(!showExamples)}
                className="flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
              >
                <Lightbulb size={12} /> Examples
              </button>

              {/* Voice button */}
              <button
                onClick={listening ? stopVoice : startVoice}
                title={listening ? 'Stop recording' : 'Start voice input'}
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  listening
                    ? 'bg-red-500 text-white animate-pulse shadow-md'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                {listening ? <MicOff size={12} /> : <Mic size={12} />}
                {listening ? 'Stop' : 'Voice'}
                {listening ? 'Stop' : ''}
              </button>

              {/* Attach file button */}
              <button
                onClick={() => fileInputRef.current?.click()}
                title="Attach file — image, PDF, or text"
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  attachedFile
                    ? 'bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                <Paperclip size={12} />
                {attachedFile ? 'Change' : 'Attach'}
              </button>
              <input
                ref={fileInputRef}
                type="file"
                className="hidden"
                accept="image/*,.pdf,.txt,.csv,.md"
                onChange={handleFile}
              />

              {(text.trim() || attachedFile) && !processing && !editData && (
                <button onClick={() => { setText(''); setAttached(null); discard(); }} className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 px-2 transition-colors">
                  Clear
                </button>
              )}

              <button
                onClick={process}
                disabled={(!text.trim() && !attachedFile) || processing || !!editData}
                className="btn-primary flex items-center gap-1.5 py-1.5 px-4 text-xs sm:text-sm disabled:opacity-40 bg-gradient-to-r from-violet-600 to-primary border-0"
              >
                {processing ? (
                  <> Parsing…</>
                ) : (
                  <><Sparkles size={13} /> Process</>
                )}
              </button>

            {/* Example suggestions */}
            {showExamples && (

Try these

                {EXAMPLES.map((ex, i) => (
                  <button key={i} onClick={() => useExample(ex)}
                    className="flex items-center gap-2 w-full text-left px-3 py-2 rounded-xl text-xs text-gray-600 dark:text-gray-400 hover:bg-white dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white transition-colors group">
                    <ArrowRight size={11} className="shrink-0 text-gray-400 group-hover:text-primary transition-colors" />
                    {ex}
                  </button>
                ))}

            )}

          {/* Parsed result */}
          {editData && (
            <ParsedCard
              data={editData}
              onChange={changeField}
              projects={projects}
              onSave={save}
              onDiscard={discard}
            />
          )}

          {/* Empty state */}
          {!editData && !processing && (

              <Sparkles size={32} className="text-gray-300 dark:text-gray-700 mb-3" />

Type, speak, or attach a file — then click Process

Claude will parse it into a task, note, or project

          )}

        {/* ── RIGHT: History ──────────────────────────────────────────── */}

Recent Captures

Last {history.length} items saved via Quick Note

          {history.length === 0 ? (

              <Clock size={28} className="text-gray-200 dark:text-gray-700 mb-3" />

Nothing captured yet

Your saved items will appear here

          ) : (

              {history.map((item, i) => <HistoryItem key={i} item={item} />)}

          )}

          {/* Tip box */}

Tips

• ⌘ Enter to process instantly

• Click Voice to dictate hands-free

• Attach a PDF, image, or text — AI reads it

  );
}

-  const [showExamples, setShowEx]   = useState(false); // unused, kept for safe removal


const useExample = (ex) => { setText(ex); setShowEx(false); textareaRef.current?.focus(); };


import { useState, useEffect, useRef } from 'react';
import {
  Sparkles, Save, Trash2, Lightbulb,
  CheckSquare, FileText, FolderKanban, Clock,
  User, Calendar, ArrowRight, Mic, MicOff,
  Paperclip, X, FileImage, FileCode, File
} from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import { CATEGORIES, PRIORITIES, getCategoryClass, getPriorityClass } from '../utils';

const HISTORY_KEY = 'quicknote_history';
function loadHistory() { try { return JSON.parse(localStorage.getItem(HISTORY_KEY) || '[]'); } catch { return []; } }
function pushHistory(item) {
  const h = loadHistory();
  h.unshift({ ...item, savedAt: new Date().toISOString() });
  localStorage.setItem(HISTORY_KEY, JSON.stringify(h.slice(0, 8)));
}

const TYPE_CONFIG = {
  task:    { icon: CheckSquare,  color: 'text-primary',    bg: 'bg-primary/10',                        label: 'Task'    },
  note:    { icon: FileText,     color: 'text-teal-600',   bg: 'bg-teal-50 dark:bg-teal-900/20',       label: 'Note'    },
  project: { icon: FolderKanban, color: 'text-violet-600', bg: 'bg-violet-50 dark:bg-violet-900/20',   label: 'Project' },
};

function fileIcon(mime) {
  if (!mime) return File;
  if (mime.startsWith('image/')) return FileImage;
  if (mime === 'application/pdf') return FileCode;
  return File;
}

// ── Parsed result card ───────────────────────────────────────────────────────
function ParsedCard({ data, onChange, onSave, onDiscard }) {
  const TypeCfg = TYPE_CONFIG[data.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;

  return (

        <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center`}>
          <TypeIcon size={15} className={TypeCfg.color} />

AI Parsed Result

Review and edit before saving

        <select
          value={data.type}
          onChange={e => onChange('type', e.target.value)}
          className="text-xs font-semibold border border-primary/30 rounded-lg px-2.5 py-1.5 bg-white dark:bg-gray-800 text-primary focus:outline-none focus:ring-2 focus:ring-primary/30"
        >
          <option value="task">Task</option>
          <option value="note">Note</option>
          <option value="project">Project</option>
        </select>

          Title
          <input className="input font-semibold text-base" value={data.title} onChange={e => onChange('title', e.target.value)} placeholder="Title…" />

            Category
            <select className="select text-sm" value={data.category} onChange={e => onChange('category', e.target.value)}>
              {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </select>

            Priority
            <select className="select text-sm" value={data.priority} onChange={e => onChange('priority', e.target.value)}>
              {PRIORITIES.map(p => <option key={p}>{p}</option>)}
            </select>

        {data.type === 'task' && (

              <User size={10} /> Assignee
              <input className="input text-sm" value={data.assignee || ''} onChange={e => onChange('assignee', e.target.value)} placeholder="Person name" />

              <Calendar size={10} /> Due Date
              <input type="date" className="input text-sm" value={data.due || ''} onChange={e => onChange('due', e.target.value)} />

        )}

          Details / Notes
          <textarea className="input resize-none text-sm" rows={3} value={data.details || ''} onChange={e => onChange('details', e.target.value)} placeholder="Context…" />

        {data.type === 'task' && (

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.isFollowUp} onChange={e => onChange('isFollowUp', e.target.checked)} />
              Follow-up needed

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.suggestedTodayFocus} onChange={e => onChange('suggestedTodayFocus', e.target.checked)} />
              Pin to today

        )}
        {data.projectHint && (

            <Lightbulb size={13} className="text-amber-600 shrink-0" />

              Suggested project: {data.projectHint}

        )}

        <button onClick={onDiscard} className="btn-ghost flex items-center gap-1.5 text-sm text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20">
          <Trash2 size={13} /> Discard
        </button>

        <button onClick={onSave} className="btn-primary flex items-center gap-2">
          <Save size={14} /> Save to Workspace
        </button>

  );
}

// ── History card ─────────────────────────────────────────────────────────────
function HistoryItem({ item }) {
  const TypeCfg = TYPE_CONFIG[item.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;
  const when = new Date(item.savedAt);
  const timeStr = when.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });

  return (

      <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center shrink-0`}>
        <TypeIcon size={14} className={TypeCfg.color} />

{item.title}

          {item.category && (
            <span className={`badge text-[10px] py-0 ${getCategoryClass(item.category)}`}>
              {CATEGORIES[item.category]?.label?.split(' ')[0]}

          )}
          {item.priority && (
            <span className={`badge text-[10px] py-0 ${getPriorityClass(item.priority)}`}>{item.priority}
          )}
          {timeStr}

  );
}

// ── Main component ────────────────────────────────────────────────────────────
export default function QuickNote() {
  const [text, setText]             = useState('');
  const [processing, setProcessing] = useState(false);
  const [editData, setEditData]     = useState(null);
  const [projects, setProjects]     = useState([]);
  const [history, setHistory]       = useState(loadHistory);
  const [listening, setListening]   = useState(false);
  const [attachedFile, setAttached] = useState(null); // { name, mimeType, dataUrl }
  const textareaRef  = useRef(null);
  const fileInputRef = useRef(null);
  const recognitionRef = useRef(null);

  useEffect(() => { api.get('/projects').then(setProjects).catch(() => {}); }, []);

  // ── Voice ────────────────────────────────────────────────────────────────
  const startVoice = () => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) { toast.error('Voice not supported in this browser'); return; }
    const rec = new SR();
    rec.continuous = true;
    rec.interimResults = true;
    rec.lang = 'en-US';
    let base = text;
    rec.onresult = (e) => {
      let interim = '';
      let final = '';
      for (let i = e.resultIndex; i < e.results.length; i++) {
        if (e.results[i].isFinal) final += e.results[i][0].transcript;
        else interim += e.results[i][0].transcript;
      }
      if (final) base = base + (base ? ' ' : '') + final;
      setText(base + (interim ? ' ' + interim : ''));
    };
    rec.onerror = () => { setListening(false); };
    rec.onend   = () => { setListening(false); };
    recognitionRef.current = rec;
    rec.start();
    setListening(true);
  };

  const stopVoice = () => {
    recognitionRef.current?.stop();
    setListening(false);
  };

  // ── File attachment ──────────────────────────────────────────────────────
  const handleFile = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      setAttached({ name: file.name, mimeType: file.type, dataUrl: ev.target.result });
      toast.success(`Attached: ${file.name}`);
    };
    reader.readAsDataURL(file);
    e.target.value = '';
  };

  const removeAttachment = () => setAttached(null);

  // ── AI processing ────────────────────────────────────────────────────────
  const process = async () => {
    if (!text.trim() && !attachedFile) { toast.error('Type something or attach a file first'); return; }
    setProcessing(true);
    try {
      let result;
      if (attachedFile) {
        result = await api.post('/ai/parse-file', {
          fileData: attachedFile.dataUrl,
          mimeType: attachedFile.mimeType,
          fileName: attachedFile.name,
          text: text.trim(),
        });
      } else {
        result = await api.post('/ai/parse', { text });
      }
      setEditData({ ...result });
    } catch (e) {
      toast.error('AI parsing failed: ' + e.message);
    } finally { setProcessing(false); }
  };

  const changeField = (k, v) => setEditData(d => ({ ...d, [k]: v }));

  // ── Save ─────────────────────────────────────────────────────────────────
  const save = async () => {
    if (!editData) return;
    try {
      const d = editData;
      if (d.type === 'task') {
        await api.post('/tasks', {
          title: d.title, notes: d.details, category: d.category, priority: d.priority,
          assignee: d.assignee || '', due_date: d.due || '',
          is_follow_up: d.isFollowUp ? 1 : 0,
          is_today_focus: d.suggestedTodayFocus ? 1 : 0,
        });
        toast.success('Task saved!');
      } else if (d.type === 'note') {
        await api.post('/notes', { title: d.title, body: d.details });
        toast.success('Note saved!');
      } else {
        await api.post('/projects', { name: d.title, category: d.category, description: d.details, priority: d.priority });
        toast.success('Project created!');
      }
      pushHistory({ type: d.type, title: d.title, category: d.category, priority: d.priority });
      setHistory(loadHistory());
      setEditData(null); setText(''); setAttached(null);
      textareaRef.current?.focus();
    } catch (e) { toast.error(e.message); }
  };

  const discard = () => setEditData(null);
  const useExample = (ex) => { setText(ex); setShowEx(false); textareaRef.current?.focus(); };

  const AttachIcon = attachedFile ? fileIcon(attachedFile.mimeType) : Paperclip;

  return (

      {/* Header — vibrant gradient */}

            <Sparkles size={15} className="text-white" />

Quick Note

Type, speak, or attach — AI structures it instantly

      {/* Main content */}

        {/* ── LEFT: Input + Parsed result ────────────────────────────── */}

          {/* Input card — gradient border accent */}

            {/* Top accent bar */}

            {/* Textarea — 3 rows on mobile, 5 on desktop */}
            <textarea
              ref={textareaRef}
              value={text}
              onChange={e => setText(e.target.value)}
              onKeyDown={e => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') process(); }}
              rows={3}
              placeholder={"Type anything — task, idea, follow-up, meeting note…"}
              className="w-full px-4 pt-4 pb-2 text-sm sm:text-base bg-transparent border-none outline-none resize-none text-gray-900 dark:text-white placeholder-gray-300 dark:placeholder-gray-600 font-medium leading-relaxed sm:rows-5"
              style={{ minHeight: '80px' }}
            />

            {/* Attached file pill */}
            {attachedFile && (

                <AttachIcon size={12} className="text-violet-600 shrink-0" />
                {attachedFile.name}
                <button onClick={removeAttachment} className="text-violet-400 hover:text-red-500 transition-colors">
                  <X size={12} />
                </button>

            )}

            {/* Toolbar */}

              {/* Examples */}
              <button
                onClick={() => setShowEx(!showExamples)}
                className="flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
              >
                <Lightbulb size={12} /> Examples
              </button>

              {/* Voice button */}
              <button
                onClick={listening ? stopVoice : startVoice}
                title={listening ? 'Stop recording' : 'Start voice input'}
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  listening
                    ? 'bg-red-500 text-white animate-pulse shadow-md'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                {listening ? <MicOff size={12} /> : <Mic size={12} />}
                {listening ? 'Stop' : 'Voice'}
                {listening ? 'Stop' : ''}
              </button>

              {/* Attach file button */}
              <button
                onClick={() => fileInputRef.current?.click()}
                title="Attach file — image, PDF, or text"
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  attachedFile
                    ? 'bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                <Paperclip size={12} />
                {attachedFile ? 'Change' : 'Attach'}
              </button>
              <input
                ref={fileInputRef}
                type="file"
                className="hidden"
                accept="image/*,.pdf,.txt,.csv,.md"
                onChange={handleFile}
              />

              {(text.trim() || attachedFile) && !processing && !editData && (
                <button onClick={() => { setText(''); setAttached(null); discard(); }} className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 px-2 transition-colors">
                  Clear
                </button>
              )}

              <button
                onClick={process}
                disabled={(!text.trim() && !attachedFile) || processing || !!editData}
                className="btn-primary flex items-center gap-1.5 py-1.5 px-4 text-xs sm:text-sm disabled:opacity-40 bg-gradient-to-r from-violet-600 to-primary border-0"
              >
                {processing ? (
                  <> Parsing…</>
                ) : (
                  <><Sparkles size={13} /> Process</>
                )}
              </button>

            {/* Example suggestions */}
            {showExamples && (

Try these

                {EXAMPLES.map((ex, i) => (
                  <button key={i} onClick={() => useExample(ex)}
                    className="flex items-center gap-2 w-full text-left px-3 py-2 rounded-xl text-xs text-gray-600 dark:text-gray-400 hover:bg-white dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white transition-colors group">
                    <ArrowRight size={11} className="shrink-0 text-gray-400 group-hover:text-primary transition-colors" />
                    {ex}
                  </button>
                ))}

            )}

          {/* Parsed result */}
          {editData && (
            <ParsedCard
              data={editData}
              onChange={changeField}
              projects={projects}
              onSave={save}
              onDiscard={discard}
            />
          )}

          {/* Empty state */}
          {!editData && !processing && (

              <Sparkles size={32} className="text-gray-300 dark:text-gray-700 mb-3" />

Type, speak, or attach a file — then click Process

Claude will parse it into a task, note, or project

          )}

        {/* ── RIGHT: History ──────────────────────────────────────────── */}

Recent Captures

Last {history.length} items saved via Quick Note

          {history.length === 0 ? (

              <Clock size={28} className="text-gray-200 dark:text-gray-700 mb-3" />

Nothing captured yet

Your saved items will appear here

          ) : (

              {history.map((item, i) => <HistoryItem key={i} item={item} />)}

          )}

          {/* Tip box */}

Tips

• ⌘ Enter to process instantly

• Click Voice to dictate hands-free

• Attach a PDF, image, or text — AI reads it

  );
}

-  const useExample = (ex) => { setText(ex); setShowEx(false); textareaRef.current?.focus(); };

+const AttachIcon = attachedFile ? fileIcon(attachedFile.mimeType) : Paperclip;

-  const AttachIcon = attachedFile ? fileIcon(attachedFile.mimeType) : Paperclip;

<div className="flex flex-col h-full overflow-hidden">


{/* Examples */}
              <button
                onClick={() => setShowEx(!showExamples)}
                className="flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
              >
                <Lightbulb size={12} /> Examples
              </button>

              {/* Voice button */}


import { useState, useEffect, useRef } from 'react';
import {
  Sparkles, Save, Trash2, Lightbulb,
  CheckSquare, FileText, FolderKanban, Clock,
  User, Calendar, ArrowRight, Mic, MicOff,
  Paperclip, X, FileImage, FileCode, File
} from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import { CATEGORIES, PRIORITIES, getCategoryClass, getPriorityClass } from '../utils';

const HISTORY_KEY = 'quicknote_history';
function loadHistory() { try { return JSON.parse(localStorage.getItem(HISTORY_KEY) || '[]'); } catch { return []; } }
function pushHistory(item) {
  const h = loadHistory();
  h.unshift({ ...item, savedAt: new Date().toISOString() });
  localStorage.setItem(HISTORY_KEY, JSON.stringify(h.slice(0, 8)));
}

const TYPE_CONFIG = {
  task:    { icon: CheckSquare,  color: 'text-primary',    bg: 'bg-primary/10',                        label: 'Task'    },
  note:    { icon: FileText,     color: 'text-teal-600',   bg: 'bg-teal-50 dark:bg-teal-900/20',       label: 'Note'    },
  project: { icon: FolderKanban, color: 'text-violet-600', bg: 'bg-violet-50 dark:bg-violet-900/20',   label: 'Project' },
};

function fileIcon(mime) {
  if (!mime) return File;
  if (mime.startsWith('image/')) return FileImage;
  if (mime === 'application/pdf') return FileCode;
  return File;
}

// ── Parsed result card ───────────────────────────────────────────────────────
function ParsedCard({ data, onChange, onSave, onDiscard }) {
  const TypeCfg = TYPE_CONFIG[data.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;

  return (

        <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center`}>
          <TypeIcon size={15} className={TypeCfg.color} />

AI Parsed Result

Review and edit before saving

        <select
          value={data.type}
          onChange={e => onChange('type', e.target.value)}
          className="text-xs font-semibold border border-primary/30 rounded-lg px-2.5 py-1.5 bg-white dark:bg-gray-800 text-primary focus:outline-none focus:ring-2 focus:ring-primary/30"
        >
          <option value="task">Task</option>
          <option value="note">Note</option>
          <option value="project">Project</option>
        </select>

          Title
          <input className="input font-semibold text-base" value={data.title} onChange={e => onChange('title', e.target.value)} placeholder="Title…" />

            Category
            <select className="select text-sm" value={data.category} onChange={e => onChange('category', e.target.value)}>
              {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </select>

            Priority
            <select className="select text-sm" value={data.priority} onChange={e => onChange('priority', e.target.value)}>
              {PRIORITIES.map(p => <option key={p}>{p}</option>)}
            </select>

        {data.type === 'task' && (

              <User size={10} /> Assignee
              <input className="input text-sm" value={data.assignee || ''} onChange={e => onChange('assignee', e.target.value)} placeholder="Person name" />

              <Calendar size={10} /> Due Date
              <input type="date" className="input text-sm" value={data.due || ''} onChange={e => onChange('due', e.target.value)} />

        )}

          Details / Notes
          <textarea className="input resize-none text-sm" rows={3} value={data.details || ''} onChange={e => onChange('details', e.target.value)} placeholder="Context…" />

        {data.type === 'task' && (

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.isFollowUp} onChange={e => onChange('isFollowUp', e.target.checked)} />
              Follow-up needed

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.suggestedTodayFocus} onChange={e => onChange('suggestedTodayFocus', e.target.checked)} />
              Pin to today

        )}
        {data.projectHint && (

            <Lightbulb size={13} className="text-amber-600 shrink-0" />

              Suggested project: {data.projectHint}

        )}

        <button onClick={onDiscard} className="btn-ghost flex items-center gap-1.5 text-sm text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20">
          <Trash2 size={13} /> Discard
        </button>

        <button onClick={onSave} className="btn-primary flex items-center gap-2">
          <Save size={14} /> Save to Workspace
        </button>

  );
}

// ── History card ─────────────────────────────────────────────────────────────
function HistoryItem({ item }) {
  const TypeCfg = TYPE_CONFIG[item.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;
  const when = new Date(item.savedAt);
  const timeStr = when.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });

  return (

      <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center shrink-0`}>
        <TypeIcon size={14} className={TypeCfg.color} />

{item.title}

          {item.category && (
            <span className={`badge text-[10px] py-0 ${getCategoryClass(item.category)}`}>
              {CATEGORIES[item.category]?.label?.split(' ')[0]}

          )}
          {item.priority && (
            <span className={`badge text-[10px] py-0 ${getPriorityClass(item.priority)}`}>{item.priority}
          )}
          {timeStr}

  );
}

// ── Main component ────────────────────────────────────────────────────────────
export default function QuickNote() {
  const [text, setText]             = useState('');
  const [processing, setProcessing] = useState(false);
  const [editData, setEditData]     = useState(null);
  const [projects, setProjects]     = useState([]);
  const [history, setHistory]       = useState(loadHistory);
  const [listening, setListening]   = useState(false);
  const [attachedFile, setAttached] = useState(null); // { name, mimeType, dataUrl }
  const textareaRef  = useRef(null);
  const fileInputRef = useRef(null);
  const recognitionRef = useRef(null);

  useEffect(() => { api.get('/projects').then(setProjects).catch(() => {}); }, []);

  // ── Voice ────────────────────────────────────────────────────────────────
  const startVoice = () => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) { toast.error('Voice not supported in this browser'); return; }
    const rec = new SR();
    rec.continuous = true;
    rec.interimResults = true;
    rec.lang = 'en-US';
    let base = text;
    rec.onresult = (e) => {
      let interim = '';
      let final = '';
      for (let i = e.resultIndex; i < e.results.length; i++) {
        if (e.results[i].isFinal) final += e.results[i][0].transcript;
        else interim += e.results[i][0].transcript;
      }
      if (final) base = base + (base ? ' ' : '') + final;
      setText(base + (interim ? ' ' + interim : ''));
    };
    rec.onerror = () => { setListening(false); };
    rec.onend   = () => { setListening(false); };
    recognitionRef.current = rec;
    rec.start();
    setListening(true);
  };

  const stopVoice = () => {
    recognitionRef.current?.stop();
    setListening(false);
  };

  // ── File attachment ──────────────────────────────────────────────────────
  const handleFile = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      setAttached({ name: file.name, mimeType: file.type, dataUrl: ev.target.result });
      toast.success(`Attached: ${file.name}`);
    };
    reader.readAsDataURL(file);
    e.target.value = '';
  };

  const removeAttachment = () => setAttached(null);

  // ── AI processing ────────────────────────────────────────────────────────
  const process = async () => {
    if (!text.trim() && !attachedFile) { toast.error('Type something or attach a file first'); return; }
    setProcessing(true);
    try {
      let result;
      if (attachedFile) {
        result = await api.post('/ai/parse-file', {
          fileData: attachedFile.dataUrl,
          mimeType: attachedFile.mimeType,
          fileName: attachedFile.name,
          text: text.trim(),
        });
      } else {
        result = await api.post('/ai/parse', { text });
      }
      setEditData({ ...result });
    } catch (e) {
      toast.error('AI parsing failed: ' + e.message);
    } finally { setProcessing(false); }
  };

  const changeField = (k, v) => setEditData(d => ({ ...d, [k]: v }));

  // ── Save ─────────────────────────────────────────────────────────────────
  const save = async () => {
    if (!editData) return;
    try {
      const d = editData;
      if (d.type === 'task') {
        await api.post('/tasks', {
          title: d.title, notes: d.details, category: d.category, priority: d.priority,
          assignee: d.assignee || '', due_date: d.due || '',
          is_follow_up: d.isFollowUp ? 1 : 0,
          is_today_focus: d.suggestedTodayFocus ? 1 : 0,
        });
        toast.success('Task saved!');
      } else if (d.type === 'note') {
        await api.post('/notes', { title: d.title, body: d.details });
        toast.success('Note saved!');
      } else {
        await api.post('/projects', { name: d.title, category: d.category, description: d.details, priority: d.priority });
        toast.success('Project created!');
      }
      pushHistory({ type: d.type, title: d.title, category: d.category, priority: d.priority });
      setHistory(loadHistory());
      setEditData(null); setText(''); setAttached(null);
      textareaRef.current?.focus();
    } catch (e) { toast.error(e.message); }
  };

  const discard = () => setEditData(null);
const AttachIcon = attachedFile ? fileIcon(attachedFile.mimeType) : Paperclip;

  return (

      {/* Header — vibrant gradient */}

            <Sparkles size={15} className="text-white" />

Quick Note

Type, speak, or attach — AI structures it instantly

      {/* Main content */}

        {/* ── LEFT: Input + Parsed result ────────────────────────────── */}

          {/* Input card — gradient border accent */}

            {/* Top accent bar */}

            {/* Textarea — 3 rows on mobile, 5 on desktop */}
            <textarea
              ref={textareaRef}
              value={text}
              onChange={e => setText(e.target.value)}
              onKeyDown={e => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') process(); }}
              rows={3}
              placeholder={"Type anything — task, idea, follow-up, meeting note…"}
              className="w-full px-4 pt-4 pb-2 text-sm sm:text-base bg-transparent border-none outline-none resize-none text-gray-900 dark:text-white placeholder-gray-300 dark:placeholder-gray-600 font-medium leading-relaxed sm:rows-5"
              style={{ minHeight: '80px' }}
            />

            {/* Attached file pill */}
            {attachedFile && (

                <AttachIcon size={12} className="text-violet-600 shrink-0" />
                {attachedFile.name}
                <button onClick={removeAttachment} className="text-violet-400 hover:text-red-500 transition-colors">
                  <X size={12} />
                </button>

            )}

            {/* Toolbar */}

              {/* Examples */}
              <button
                onClick={() => setShowEx(!showExamples)}
                className="flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
              >
                <Lightbulb size={12} /> Examples
              </button>

              {/* Voice button */}
              <button
                onClick={listening ? stopVoice : startVoice}
                title={listening ? 'Stop recording' : 'Start voice input'}
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  listening
                    ? 'bg-red-500 text-white animate-pulse shadow-md'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                {listening ? <MicOff size={12} /> : <Mic size={12} />}
                {listening ? 'Stop' : 'Voice'}
                {listening ? 'Stop' : ''}
              </button>

              {/* Attach file button */}
              <button
                onClick={() => fileInputRef.current?.click()}
                title="Attach file — image, PDF, or text"
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  attachedFile
                    ? 'bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                <Paperclip size={12} />
                {attachedFile ? 'Change' : 'Attach'}
              </button>
              <input
                ref={fileInputRef}
                type="file"
                className="hidden"
                accept="image/*,.pdf,.txt,.csv,.md"
                onChange={handleFile}
              />

              {(text.trim() || attachedFile) && !processing && !editData && (
                <button onClick={() => { setText(''); setAttached(null); discard(); }} className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 px-2 transition-colors">
                  Clear
                </button>
              )}

              <button
                onClick={process}
                disabled={(!text.trim() && !attachedFile) || processing || !!editData}
                className="btn-primary flex items-center gap-1.5 py-1.5 px-4 text-xs sm:text-sm disabled:opacity-40 bg-gradient-to-r from-violet-600 to-primary border-0"
              >
                {processing ? (
                  <> Parsing…</>
                ) : (
                  <><Sparkles size={13} /> Process</>
                )}
              </button>

            {/* Example suggestions */}
            {showExamples && (

Try these

                {EXAMPLES.map((ex, i) => (
                  <button key={i} onClick={() => useExample(ex)}
                    className="flex items-center gap-2 w-full text-left px-3 py-2 rounded-xl text-xs text-gray-600 dark:text-gray-400 hover:bg-white dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white transition-colors group">
                    <ArrowRight size={11} className="shrink-0 text-gray-400 group-hover:text-primary transition-colors" />
                    {ex}
                  </button>
                ))}

            )}

          {/* Parsed result */}
          {editData && (
            <ParsedCard
              data={editData}
              onChange={changeField}
              projects={projects}
              onSave={save}
              onDiscard={discard}
            />
          )}

          {/* Empty state */}
          {!editData && !processing && (

              <Sparkles size={32} className="text-gray-300 dark:text-gray-700 mb-3" />

Type, speak, or attach a file — then click Process

Claude will parse it into a task, note, or project

          )}

        {/* ── RIGHT: History ──────────────────────────────────────────── */}

Recent Captures

Last {history.length} items saved via Quick Note

          {history.length === 0 ? (

              <Clock size={28} className="text-gray-200 dark:text-gray-700 mb-3" />

Nothing captured yet

Your saved items will appear here

          ) : (

              {history.map((item, i) => <HistoryItem key={i} item={item} />)}

          )}

          {/* Tip box */}

Tips

• ⌘ Enter to process instantly

• Click Voice to dictate hands-free

• Attach a PDF, image, or text — AI reads it

  );
}

<div className="flex items-center gap-1.5 px-3 pb-3 pt-1 flex-wrap">

-                onClick={() => setShowEx(!showExamples)}

-                className="flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"

-                <Lightbulb size={12} /> Examples


{/* Example suggestions */}
            {showExamples && (

Try these

                {EXAMPLES.map((ex, i) => (
                  <button key={i} onClick={() => useExample(ex)}
                    className="flex items-center gap-2 w-full text-left px-3 py-2 rounded-xl text-xs text-gray-600 dark:text-gray-400 hover:bg-white dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white transition-colors group">
                    <ArrowRight size={11} className="shrink-0 text-gray-400 group-hover:text-primary transition-colors" />
                    {ex}
                  </button>
                ))}

            )}


import { useState, useEffect, useRef } from 'react';
import {
  Sparkles, Save, Trash2, Lightbulb,
  CheckSquare, FileText, FolderKanban, Clock,
  User, Calendar, ArrowRight, Mic, MicOff,
  Paperclip, X, FileImage, FileCode, File
} from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import { CATEGORIES, PRIORITIES, getCategoryClass, getPriorityClass } from '../utils';

const HISTORY_KEY = 'quicknote_history';
function loadHistory() { try { return JSON.parse(localStorage.getItem(HISTORY_KEY) || '[]'); } catch { return []; } }
function pushHistory(item) {
  const h = loadHistory();
  h.unshift({ ...item, savedAt: new Date().toISOString() });
  localStorage.setItem(HISTORY_KEY, JSON.stringify(h.slice(0, 8)));
}

const TYPE_CONFIG = {
  task:    { icon: CheckSquare,  color: 'text-primary',    bg: 'bg-primary/10',                        label: 'Task'    },
  note:    { icon: FileText,     color: 'text-teal-600',   bg: 'bg-teal-50 dark:bg-teal-900/20',       label: 'Note'    },
  project: { icon: FolderKanban, color: 'text-violet-600', bg: 'bg-violet-50 dark:bg-violet-900/20',   label: 'Project' },
};

function fileIcon(mime) {
  if (!mime) return File;
  if (mime.startsWith('image/')) return FileImage;
  if (mime === 'application/pdf') return FileCode;
  return File;
}

// ── Parsed result card ───────────────────────────────────────────────────────
function ParsedCard({ data, onChange, onSave, onDiscard }) {
  const TypeCfg = TYPE_CONFIG[data.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;

  return (

        <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center`}>
          <TypeIcon size={15} className={TypeCfg.color} />

AI Parsed Result

Review and edit before saving

        <select
          value={data.type}
          onChange={e => onChange('type', e.target.value)}
          className="text-xs font-semibold border border-primary/30 rounded-lg px-2.5 py-1.5 bg-white dark:bg-gray-800 text-primary focus:outline-none focus:ring-2 focus:ring-primary/30"
        >
          <option value="task">Task</option>
          <option value="note">Note</option>
          <option value="project">Project</option>
        </select>

          Title
          <input className="input font-semibold text-base" value={data.title} onChange={e => onChange('title', e.target.value)} placeholder="Title…" />

            Category
            <select className="select text-sm" value={data.category} onChange={e => onChange('category', e.target.value)}>
              {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </select>

            Priority
            <select className="select text-sm" value={data.priority} onChange={e => onChange('priority', e.target.value)}>
              {PRIORITIES.map(p => <option key={p}>{p}</option>)}
            </select>

        {data.type === 'task' && (

              <User size={10} /> Assignee
              <input className="input text-sm" value={data.assignee || ''} onChange={e => onChange('assignee', e.target.value)} placeholder="Person name" />

              <Calendar size={10} /> Due Date
              <input type="date" className="input text-sm" value={data.due || ''} onChange={e => onChange('due', e.target.value)} />

        )}

          Details / Notes
          <textarea className="input resize-none text-sm" rows={3} value={data.details || ''} onChange={e => onChange('details', e.target.value)} placeholder="Context…" />

        {data.type === 'task' && (

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.isFollowUp} onChange={e => onChange('isFollowUp', e.target.checked)} />
              Follow-up needed

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.suggestedTodayFocus} onChange={e => onChange('suggestedTodayFocus', e.target.checked)} />
              Pin to today

        )}
        {data.projectHint && (

            <Lightbulb size={13} className="text-amber-600 shrink-0" />

              Suggested project: {data.projectHint}

        )}

        <button onClick={onDiscard} className="btn-ghost flex items-center gap-1.5 text-sm text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20">
          <Trash2 size={13} /> Discard
        </button>

        <button onClick={onSave} className="btn-primary flex items-center gap-2">
          <Save size={14} /> Save to Workspace
        </button>

  );
}

// ── History card ─────────────────────────────────────────────────────────────
function HistoryItem({ item }) {
  const TypeCfg = TYPE_CONFIG[item.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;
  const when = new Date(item.savedAt);
  const timeStr = when.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });

  return (

      <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center shrink-0`}>
        <TypeIcon size={14} className={TypeCfg.color} />

{item.title}

          {item.category && (
            <span className={`badge text-[10px] py-0 ${getCategoryClass(item.category)}`}>
              {CATEGORIES[item.category]?.label?.split(' ')[0]}

          )}
          {item.priority && (
            <span className={`badge text-[10px] py-0 ${getPriorityClass(item.priority)}`}>{item.priority}
          )}
          {timeStr}

  );
}

// ── Main component ────────────────────────────────────────────────────────────
export default function QuickNote() {
  const [text, setText]             = useState('');
  const [processing, setProcessing] = useState(false);
  const [editData, setEditData]     = useState(null);
  const [projects, setProjects]     = useState([]);
  const [history, setHistory]       = useState(loadHistory);
  const [listening, setListening]   = useState(false);
  const [attachedFile, setAttached] = useState(null); // { name, mimeType, dataUrl }
  const textareaRef  = useRef(null);
  const fileInputRef = useRef(null);
  const recognitionRef = useRef(null);

  useEffect(() => { api.get('/projects').then(setProjects).catch(() => {}); }, []);

  // ── Voice ────────────────────────────────────────────────────────────────
  const startVoice = () => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) { toast.error('Voice not supported in this browser'); return; }
    const rec = new SR();
    rec.continuous = true;
    rec.interimResults = true;
    rec.lang = 'en-US';
    let base = text;
    rec.onresult = (e) => {
      let interim = '';
      let final = '';
      for (let i = e.resultIndex; i < e.results.length; i++) {
        if (e.results[i].isFinal) final += e.results[i][0].transcript;
        else interim += e.results[i][0].transcript;
      }
      if (final) base = base + (base ? ' ' : '') + final;
      setText(base + (interim ? ' ' + interim : ''));
    };
    rec.onerror = () => { setListening(false); };
    rec.onend   = () => { setListening(false); };
    recognitionRef.current = rec;
    rec.start();
    setListening(true);
  };

  const stopVoice = () => {
    recognitionRef.current?.stop();
    setListening(false);
  };

  // ── File attachment ──────────────────────────────────────────────────────
  const handleFile = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      setAttached({ name: file.name, mimeType: file.type, dataUrl: ev.target.result });
      toast.success(`Attached: ${file.name}`);
    };
    reader.readAsDataURL(file);
    e.target.value = '';
  };

  const removeAttachment = () => setAttached(null);

  // ── AI processing ────────────────────────────────────────────────────────
  const process = async () => {
    if (!text.trim() && !attachedFile) { toast.error('Type something or attach a file first'); return; }
    setProcessing(true);
    try {
      let result;
      if (attachedFile) {
        result = await api.post('/ai/parse-file', {
          fileData: attachedFile.dataUrl,
          mimeType: attachedFile.mimeType,
          fileName: attachedFile.name,
          text: text.trim(),
        });
      } else {
        result = await api.post('/ai/parse', { text });
      }
      setEditData({ ...result });
    } catch (e) {
      toast.error('AI parsing failed: ' + e.message);
    } finally { setProcessing(false); }
  };

  const changeField = (k, v) => setEditData(d => ({ ...d, [k]: v }));

  // ── Save ─────────────────────────────────────────────────────────────────
  const save = async () => {
    if (!editData) return;
    try {
      const d = editData;
      if (d.type === 'task') {
        await api.post('/tasks', {
          title: d.title, notes: d.details, category: d.category, priority: d.priority,
          assignee: d.assignee || '', due_date: d.due || '',
          is_follow_up: d.isFollowUp ? 1 : 0,
          is_today_focus: d.suggestedTodayFocus ? 1 : 0,
        });
        toast.success('Task saved!');
      } else if (d.type === 'note') {
        await api.post('/notes', { title: d.title, body: d.details });
        toast.success('Note saved!');
      } else {
        await api.post('/projects', { name: d.title, category: d.category, description: d.details, priority: d.priority });
        toast.success('Project created!');
      }
      pushHistory({ type: d.type, title: d.title, category: d.category, priority: d.priority });
      setHistory(loadHistory());
      setEditData(null); setText(''); setAttached(null);
      textareaRef.current?.focus();
    } catch (e) { toast.error(e.message); }
  };

  const discard = () => setEditData(null);
const AttachIcon = attachedFile ? fileIcon(attachedFile.mimeType) : Paperclip;

  return (

      {/* Header — vibrant gradient */}

            <Sparkles size={15} className="text-white" />

Quick Note

Type, speak, or attach — AI structures it instantly

      {/* Main content */}

        {/* ── LEFT: Input + Parsed result ────────────────────────────── */}

          {/* Input card — gradient border accent */}

            {/* Top accent bar */}

            {/* Textarea — 3 rows on mobile, 5 on desktop */}
            <textarea
              ref={textareaRef}
              value={text}
              onChange={e => setText(e.target.value)}
              onKeyDown={e => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') process(); }}
              rows={3}
              placeholder={"Type anything — task, idea, follow-up, meeting note…"}
              className="w-full px-4 pt-4 pb-2 text-sm sm:text-base bg-transparent border-none outline-none resize-none text-gray-900 dark:text-white placeholder-gray-300 dark:placeholder-gray-600 font-medium leading-relaxed sm:rows-5"
              style={{ minHeight: '80px' }}
            />

            {/* Attached file pill */}
            {attachedFile && (

                <AttachIcon size={12} className="text-violet-600 shrink-0" />
                {attachedFile.name}
                <button onClick={removeAttachment} className="text-violet-400 hover:text-red-500 transition-colors">
                  <X size={12} />
                </button>

            )}

            {/* Toolbar */}

              {/* Voice button */}
              <button
                onClick={listening ? stopVoice : startVoice}
                title={listening ? 'Stop recording' : 'Start voice input'}
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  listening
                    ? 'bg-red-500 text-white animate-pulse shadow-md'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                {listening ? <MicOff size={12} /> : <Mic size={12} />}
                {listening ? 'Stop' : 'Voice'}
                {listening ? 'Stop' : ''}
              </button>

              {/* Attach file button */}
              <button
                onClick={() => fileInputRef.current?.click()}
                title="Attach file — image, PDF, or text"
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  attachedFile
                    ? 'bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                <Paperclip size={12} />
                {attachedFile ? 'Change' : 'Attach'}
              </button>
              <input
                ref={fileInputRef}
                type="file"
                className="hidden"
                accept="image/*,.pdf,.txt,.csv,.md"
                onChange={handleFile}
              />

              {(text.trim() || attachedFile) && !processing && !editData && (
                <button onClick={() => { setText(''); setAttached(null); discard(); }} className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 px-2 transition-colors">
                  Clear
                </button>
              )}

              <button
                onClick={process}
                disabled={(!text.trim() && !attachedFile) || processing || !!editData}
                className="btn-primary flex items-center gap-1.5 py-1.5 px-4 text-xs sm:text-sm disabled:opacity-40 bg-gradient-to-r from-violet-600 to-primary border-0"
              >
                {processing ? (
                  <> Parsing…</>
                ) : (
                  <><Sparkles size={13} /> Process</>
                )}
              </button>

            {/* Example suggestions */}
            {showExamples && (

Try these

                {EXAMPLES.map((ex, i) => (
                  <button key={i} onClick={() => useExample(ex)}
                    className="flex items-center gap-2 w-full text-left px-3 py-2 rounded-xl text-xs text-gray-600 dark:text-gray-400 hover:bg-white dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white transition-colors group">
                    <ArrowRight size={11} className="shrink-0 text-gray-400 group-hover:text-primary transition-colors" />
                    {ex}
                  </button>
                ))}

            )}

          {/* Parsed result */}
          {editData && (
            <ParsedCard
              data={editData}
              onChange={changeField}
              projects={projects}
              onSave={save}
              onDiscard={discard}
            />
          )}

          {/* Empty state */}
          {!editData && !processing && (

              <Sparkles size={32} className="text-gray-300 dark:text-gray-700 mb-3" />

Type, speak, or attach a file — then click Process

Claude will parse it into a task, note, or project

          )}

        {/* ── RIGHT: History ──────────────────────────────────────────── */}

Recent Captures

Last {history.length} items saved via Quick Note

          {history.length === 0 ? (

              <Clock size={28} className="text-gray-200 dark:text-gray-700 mb-3" />

Nothing captured yet

Your saved items will appear here

          ) : (

              {history.map((item, i) => <HistoryItem key={i} item={item} />)}

          )}

          {/* Tip box */}

Tips

• ⌘ Enter to process instantly

• Click Voice to dictate hands-free

• Attach a PDF, image, or text — AI reads it

  );
}

-            {/* Example suggestions */}

-              <div className="border-t border-gray-100 dark:border-gray-800 px-4 py-3 space-y-1.5 bg-gray-50 dark:bg-gray-800/50">

-                {EXAMPLES.map((ex, i) => (

-                  <button key={i} onClick={() => useExample(ex)}

-                    className="flex items-center gap-2 w-full text-left px-3 py-2 rounded-xl text-xs text-gray-600 dark:text-gray-400 hover:bg-white dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-white transition-colors group">

-                    <ArrowRight size={11} className="shrink-0 text-gray-400 group-hover:text-primary transition-colors" />


Now remove unused imports (`Lightbulb`, `ArrowRight`):


  Sparkles, Save, Trash2, Lightbulb,
  CheckSquare, FileText, FolderKanban, Clock,
  User, Calendar, ArrowRight, Mic, MicOff,

  Sparkles, Save, Trash2, Lightbulb,
  CheckSquare, FileText, FolderKanban, Clock,
  User, Calendar, Mic, MicOff,


  Sparkles, Save, Trash2, Lightbulb,
  CheckSquare, FileText, FolderKanban, Clock,
  User, Calendar, Mic, MicOff,

import { useState, useEffect, useRef } from 'react';
import {
  Sparkles, Save, Trash2, Lightbulb,
  CheckSquare, FileText, FolderKanban, Clock,
  User, Calendar, ArrowRight, Mic, MicOff,
  Paperclip, X, FileImage, FileCode, File
} from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import { CATEGORIES, PRIORITIES, getCategoryClass, getPriorityClass } from '../utils';

const HISTORY_KEY = 'quicknote_history';
function loadHistory() { try { return JSON.parse(localStorage.getItem(HISTORY_KEY) || '[]'); } catch { return []; } }
function pushHistory(item) {
  const h = loadHistory();
  h.unshift({ ...item, savedAt: new Date().toISOString() });
  localStorage.setItem(HISTORY_KEY, JSON.stringify(h.slice(0, 8)));
}

const TYPE_CONFIG = {
  task:    { icon: CheckSquare,  color: 'text-primary',    bg: 'bg-primary/10',                        label: 'Task'    },
  note:    { icon: FileText,     color: 'text-teal-600',   bg: 'bg-teal-50 dark:bg-teal-900/20',       label: 'Note'    },
  project: { icon: FolderKanban, color: 'text-violet-600', bg: 'bg-violet-50 dark:bg-violet-900/20',   label: 'Project' },
};

function fileIcon(mime) {
  if (!mime) return File;
  if (mime.startsWith('image/')) return FileImage;
  if (mime === 'application/pdf') return FileCode;
  return File;
}

// ── Parsed result card ───────────────────────────────────────────────────────
function ParsedCard({ data, onChange, onSave, onDiscard }) {
  const TypeCfg = TYPE_CONFIG[data.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;

  return (

        <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center`}>
          <TypeIcon size={15} className={TypeCfg.color} />

AI Parsed Result

Review and edit before saving

        <select
          value={data.type}
          onChange={e => onChange('type', e.target.value)}
          className="text-xs font-semibold border border-primary/30 rounded-lg px-2.5 py-1.5 bg-white dark:bg-gray-800 text-primary focus:outline-none focus:ring-2 focus:ring-primary/30"
        >
          <option value="task">Task</option>
          <option value="note">Note</option>
          <option value="project">Project</option>
        </select>

          Title
          <input className="input font-semibold text-base" value={data.title} onChange={e => onChange('title', e.target.value)} placeholder="Title…" />

            Category
            <select className="select text-sm" value={data.category} onChange={e => onChange('category', e.target.value)}>
              {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </select>

            Priority
            <select className="select text-sm" value={data.priority} onChange={e => onChange('priority', e.target.value)}>
              {PRIORITIES.map(p => <option key={p}>{p}</option>)}
            </select>

        {data.type === 'task' && (

              <User size={10} /> Assignee
              <input className="input text-sm" value={data.assignee || ''} onChange={e => onChange('assignee', e.target.value)} placeholder="Person name" />

              <Calendar size={10} /> Due Date
              <input type="date" className="input text-sm" value={data.due || ''} onChange={e => onChange('due', e.target.value)} />

        )}

          Details / Notes
          <textarea className="input resize-none text-sm" rows={3} value={data.details || ''} onChange={e => onChange('details', e.target.value)} placeholder="Context…" />

        {data.type === 'task' && (

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.isFollowUp} onChange={e => onChange('isFollowUp', e.target.checked)} />
              Follow-up needed

              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.suggestedTodayFocus} onChange={e => onChange('suggestedTodayFocus', e.target.checked)} />
              Pin to today

        )}
        {data.projectHint && (

            <Lightbulb size={13} className="text-amber-600 shrink-0" />

              Suggested project: {data.projectHint}

        )}

        <button onClick={onDiscard} className="btn-ghost flex items-center gap-1.5 text-sm text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20">
          <Trash2 size={13} /> Discard
        </button>

        <button onClick={onSave} className="btn-primary flex items-center gap-2">
          <Save size={14} /> Save to Workspace
        </button>

  );
}

// ── History card ─────────────────────────────────────────────────────────────
function HistoryItem({ item }) {
  const TypeCfg = TYPE_CONFIG[item.type] || TYPE_CONFIG.task;
  const TypeIcon = TypeCfg.icon;
  const when = new Date(item.savedAt);
  const timeStr = when.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });

  return (

      <div className={`w-8 h-8 rounded-xl ${TypeCfg.bg} flex items-center justify-center shrink-0`}>
        <TypeIcon size={14} className={TypeCfg.color} />

{item.title}

          {item.category && (
            <span className={`badge text-[10px] py-0 ${getCategoryClass(item.category)}`}>
              {CATEGORIES[item.category]?.label?.split(' ')[0]}

          )}
          {item.priority && (
            <span className={`badge text-[10px] py-0 ${getPriorityClass(item.priority)}`}>{item.priority}
          )}
          {timeStr}

  );
}

// ── Main component ────────────────────────────────────────────────────────────
export default function QuickNote() {
  const [text, setText]             = useState('');
  const [processing, setProcessing] = useState(false);
  const [editData, setEditData]     = useState(null);
  const [projects, setProjects]     = useState([]);
  const [history, setHistory]       = useState(loadHistory);
  const [listening, setListening]   = useState(false);
  const [attachedFile, setAttached] = useState(null); // { name, mimeType, dataUrl }
  const textareaRef  = useRef(null);
  const fileInputRef = useRef(null);
  const recognitionRef = useRef(null);

  useEffect(() => { api.get('/projects').then(setProjects).catch(() => {}); }, []);

  // ── Voice ────────────────────────────────────────────────────────────────
  const startVoice = () => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) { toast.error('Voice not supported in this browser'); return; }
    const rec = new SR();
    rec.continuous = true;
    rec.interimResults = true;
    rec.lang = 'en-US';
    let base = text;
    rec.onresult = (e) => {
      let interim = '';
      let final = '';
      for (let i = e.resultIndex; i < e.results.length; i++) {
        if (e.results[i].isFinal) final += e.results[i][0].transcript;
        else interim += e.results[i][0].transcript;
      }
      if (final) base = base + (base ? ' ' : '') + final;
      setText(base + (interim ? ' ' + interim : ''));
    };
    rec.onerror = () => { setListening(false); };
    rec.onend   = () => { setListening(false); };
    recognitionRef.current = rec;
    rec.start();
    setListening(true);
  };

  const stopVoice = () => {
    recognitionRef.current?.stop();
    setListening(false);
  };

  // ── File attachment ──────────────────────────────────────────────────────
  const handleFile = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      setAttached({ name: file.name, mimeType: file.type, dataUrl: ev.target.result });
      toast.success(`Attached: ${file.name}`);
    };
    reader.readAsDataURL(file);
    e.target.value = '';
  };

  const removeAttachment = () => setAttached(null);

  // ── AI processing ────────────────────────────────────────────────────────
  const process = async () => {
    if (!text.trim() && !attachedFile) { toast.error('Type something or attach a file first'); return; }
    setProcessing(true);
    try {
      let result;
      if (attachedFile) {
        result = await api.post('/ai/parse-file', {
          fileData: attachedFile.dataUrl,
          mimeType: attachedFile.mimeType,
          fileName: attachedFile.name,
          text: text.trim(),
        });
      } else {
        result = await api.post('/ai/parse', { text });
      }
      setEditData({ ...result });
    } catch (e) {
      toast.error('AI parsing failed: ' + e.message);
    } finally { setProcessing(false); }
  };

  const changeField = (k, v) => setEditData(d => ({ ...d, [k]: v }));

  // ── Save ─────────────────────────────────────────────────────────────────
  const save = async () => {
    if (!editData) return;
    try {
      const d = editData;
      if (d.type === 'task') {
        await api.post('/tasks', {
          title: d.title, notes: d.details, category: d.category, priority: d.priority,
          assignee: d.assignee || '', due_date: d.due || '',
          is_follow_up: d.isFollowUp ? 1 : 0,
          is_today_focus: d.suggestedTodayFocus ? 1 : 0,
        });
        toast.success('Task saved!');
      } else if (d.type === 'note') {
        await api.post('/notes', { title: d.title, body: d.details });
        toast.success('Note saved!');
      } else {
        await api.post('/projects', { name: d.title, category: d.category, description: d.details, priority: d.priority });
        toast.success('Project created!');
      }
      pushHistory({ type: d.type, title: d.title, category: d.category, priority: d.priority });
      setHistory(loadHistory());
      setEditData(null); setText(''); setAttached(null);
      textareaRef.current?.focus();
    } catch (e) { toast.error(e.message); }
  };

  const discard = () => setEditData(null);
const AttachIcon = attachedFile ? fileIcon(attachedFile.mimeType) : Paperclip;

  return (

      {/* Header — vibrant gradient */}

            <Sparkles size={15} className="text-white" />

Quick Note

Type, speak, or attach — AI structures it instantly

      {/* Main content */}

        {/* ── LEFT: Input + Parsed result ────────────────────────────── */}

          {/* Input card — gradient border accent */}

            {/* Top accent bar */}

            {/* Textarea — 3 rows on mobile, 5 on desktop */}
            <textarea
              ref={textareaRef}
              value={text}
              onChange={e => setText(e.target.value)}
              onKeyDown={e => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') process(); }}
              rows={3}
              placeholder={"Type anything — task, idea, follow-up, meeting note…"}
              className="w-full px-4 pt-4 pb-2 text-sm sm:text-base bg-transparent border-none outline-none resize-none text-gray-900 dark:text-white placeholder-gray-300 dark:placeholder-gray-600 font-medium leading-relaxed sm:rows-5"
              style={{ minHeight: '80px' }}
            />

            {/* Attached file pill */}
            {attachedFile && (

                <AttachIcon size={12} className="text-violet-600 shrink-0" />
                {attachedFile.name}
                <button onClick={removeAttachment} className="text-violet-400 hover:text-red-500 transition-colors">
                  <X size={12} />
                </button>

            )}

            {/* Toolbar */}

              {/* Voice button */}
              <button
                onClick={listening ? stopVoice : startVoice}
                title={listening ? 'Stop recording' : 'Start voice input'}
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  listening
                    ? 'bg-red-500 text-white animate-pulse shadow-md'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                {listening ? <MicOff size={12} /> : <Mic size={12} />}
                {listening ? 'Stop' : 'Voice'}
                {listening ? 'Stop' : ''}
              </button>

              {/* Attach file button */}
              <button
                onClick={() => fileInputRef.current?.click()}
                title="Attach file — image, PDF, or text"
                className={`flex items-center gap-1 px-2.5 py-1.5 rounded-xl text-xs font-medium transition-all ${
                  attachedFile
                    ? 'bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800'
                }`}
              >
                <Paperclip size={12} />
                {attachedFile ? 'Change' : 'Attach'}
              </button>
              <input
                ref={fileInputRef}
                type="file"
                className="hidden"
                accept="image/*,.pdf,.txt,.csv,.md"
                onChange={handleFile}
              />

              {(text.trim() || attachedFile) && !processing && !editData && (
                <button onClick={() => { setText(''); setAttached(null); discard(); }} className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 px-2 transition-colors">
                  Clear
                </button>
              )}

              <button
                onClick={process}
                disabled={(!text.trim() && !attachedFile) || processing || !!editData}
                className="btn-primary flex items-center gap-1.5 py-1.5 px-4 text-xs sm:text-sm disabled:opacity-40 bg-gradient-to-r from-violet-600 to-primary border-0"
              >
                {processing ? (
                  <> Parsing…</>
                ) : (
                  <><Sparkles size={13} /> Process</>
                )}
              </button>

          {/* Parsed result */}
          {editData && (
            <ParsedCard
              data={editData}
              onChange={changeField}
              projects={projects}
              onSave={save}
              onDiscard={discard}
            />
          )}

          {/* Empty state */}
          {!editData && !processing && (

              <Sparkles size={32} className="text-gray-300 dark:text-gray-700 mb-3" />

Type, speak, or attach a file — then click Process

Claude will parse it into a task, note, or project

          )}

        {/* ── RIGHT: History ──────────────────────────────────────────── */}

Recent Captures

Last {history.length} items saved via Quick Note

          {history.length === 0 ? (

              <Clock size={28} className="text-gray-200 dark:text-gray-700 mb-3" />

Nothing captured yet

Your saved items will appear here

          ) : (

              {history.map((item, i) => <HistoryItem key={i} item={item} />)}

          )}

          {/* Tip box */}

Tips

• ⌘ Enter to process instantly

• Click Voice to dictate hands-free

• Attach a PDF, image, or text — AI reads it

  );
}

-  User, Calendar, ArrowRight, Mic, MicOff,


3:  Sparkles, Save, Trash2, Lightbulb,
109:            <Lightbulb size={13} className="text-amber-600 shrink-0" />


Good — `Lightbulb` is still used in `ParsedCard` for the project hint. Now rebuild:


Bash

npm run build 2>&1 | tail -5 && pm2 restart "MY Workspace" 2>&1 | tail -3

Rebuild and restart


computing gzip size...
../dist/index.html                   0.67 kB │ gzip:   0.39 kB
../dist/assets/index-GEkcyJLe.css   58.85 kB │ gzip:   9.11 kB
../dist/assets/index-CMpRZNGz.js   371.48 kB │ gzip: 104.48 kB
✓ built in 1.13s
├────┼─────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0  │ MY Workspace    │ default     │ 1.0.0   │ fork    │ 36848    │ 0s     │ 2    │ online    │ 0%       │ 576.0kb  │ kha… │ disabled │
└────┴─────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘


Done. The Examples button and dropdown are fully removed from Quick Note. Live at `https://my-workspace.tailbd7b77.ts.net:8443`.


when i attach any phote it shows me error something related the Anthropic API key not able to upload any attachements through quick chat


pm2 logs "MY Workspace" --lines 30 --nostream 2>&1

Check pm2 logs for the error


cat /Users/khalidbinkalban/my-workspace/server/.env 2>/dev/null || echo "NO .env FILE"

Check if .env file exists with API key