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

import { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import {
  ArrowLeft, Pencil, Plus, Trash2, FileText, CheckSquare,
  Link2, Search, X, CheckCircle2, Clock, Loader2, PauseCircle,
  FolderKanban, AlignLeft, RefreshCw, ChevronRight, Calendar, User
} from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import TaskCard from './TaskCard';
import TaskModal from './TaskModal';
import ConfirmDialog from './ConfirmDialog';
import { CATEGORIES, STATUSES, PRIORITIES, getCategoryClass, getPriorityClass, getStatusClass, formatDate } from '../utils';

const CATEGORY_ACCENT = {
  ai:        'from-blue-500 to-cyan-500',
  vendor:    'from-purple-500 to-violet-500',
  gov:       'from-amber-500 to-yellow-500',
  people:    'from-teal-500 to-emerald-500',
  events:    'from-orange-500 to-red-400',
  strategic: 'from-green-500 to-teal-500',
  adhoc:     'from-red-500 to-rose-500',
};

const CATEGORY_BG = {
  ai:        'bg-blue-50 dark:bg-blue-900/20',
  vendor:    'bg-purple-50 dark:bg-purple-900/20',
  gov:       'bg-amber-50 dark:bg-amber-900/20',
  people:    'bg-teal-50 dark:bg-teal-900/20',
  events:    'bg-orange-50 dark:bg-orange-900/20',
  strategic: 'bg-green-50 dark:bg-green-900/20',
  adhoc:     'bg-red-50 dark:bg-red-900/20',
};

const STATUS_META = {
  Open:          { icon: Clock,        color: 'text-gray-500',  bg: 'bg-gray-100 dark:bg-gray-800'         },
  'In Progress': { icon: Loader2,      color: 'text-blue-600',  bg: 'bg-blue-100 dark:bg-blue-900/30'      },
  Waiting:       { icon: PauseCircle,  color: 'text-amber-600', bg: 'bg-amber-100 dark:bg-amber-900/30'    },
  Done:          { icon: CheckCircle2, color: 'text-green-600', bg: 'bg-green-100 dark:bg-green-900/30'    },
};

function InlineEdit({ value, onSave, multiline = false, placeholder = '', className = '' }) {
  const [editing, setEditing] = useState(false);
  const [val, setVal] = useState(value || '');
  useEffect(() => setVal(value || ''), [value]);
  const save = () => { setEditing(false); if (val !== value) onSave(val); };
  if (!editing) return (
     setEditing(true)}
      className={`group cursor-text flex items-start gap-1.5 ${className}`}>

        {val || placeholder}

      <Pencil size={11} className="opacity-0 group-hover:opacity-40 shrink-0 mt-0.5 text-gray-400" />

  );
  return multiline
    ? <textarea autoFocus className="input w-full resize-none text-sm" rows={4} value={val}
        onChange={e => setVal(e.target.value)} onBlur={save} />
    : <input autoFocus className="input w-full text-sm" value={val}
        onChange={e => setVal(e.target.value)} onBlur={save} onKeyDown={e => e.key === 'Enter' && save()} />;
}

function InfoRow({ icon: Icon, label, children }) {
  return (

        <Icon size={11} />{label}

{children}

  );
}

function NoteForm({ projectId, onSave, onCancel }) {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');
  const submit = async (e) => {
    e.preventDefault();
    if (!title.trim()) return;
    await onSave({ title, body, project_id: projectId, date: new Date().toISOString().split('T')[0] });
  };
  return (
    <form onSubmit={submit} className="bg-white dark:bg-gray-900 rounded-2xl border-2 border-dashed border-primary/30 p-4 space-y-3">
      <input className="input font-medium" placeholder="Note title…" value={title} onChange={e => setTitle(e.target.value)} autoFocus required />
      <textarea className="input resize-none text-sm" rows={4} placeholder="Write your note…" value={body} onChange={e => setBody(e.target.value)} />

        <button type="button" onClick={onCancel} className="btn-secondary text-xs py-1.5 px-3">Cancel</button>
        <button type="submit" className="btn-primary text-xs py-1.5 px-3">Save Note</button>

    </form>
  );
}

export default function ProjectDetail() {
  const { id } = useParams();
  const navigate = useNavigate();
  const [project, setProject]       = useState(null);
  const [tasks, setTasks]           = useState([]);
  const [notes, setNotes]           = useState([]);
  const [projects, setProjects]     = useState([]);
  const [tab, setTab]               = useState('tasks');
  const [loading, setLoading]       = useState(true);
  const [editTask, setEditTask]     = useState(null);
  const [deleteTask, setDeleteTask] = useState(null);
  const [deleteNote, setDeleteNote] = useState(null);
  const [showNewTask, setShowNewTask]   = useState(false);
  const [showNewNote, setShowNewNote]   = useState(false);
  const [showLinkTask, setShowLinkTask] = useState(false);
  const [allTasks, setAllTasks]     = useState([]);
  const [linkSearch, setLinkSearch] = useState('');

  const load = async () => {
    try {
      const [p, t, n, pr] = await Promise.all([
        api.get(`/projects/${id}`),
        api.get(`/tasks?project_id=${id}`),
        api.get(`/notes?project_id=${id}`),
        api.get('/projects'),
      ]);
      setProject(p); setTasks(t); setNotes(n); setProjects(pr);
    } catch { navigate('/projects'); }
    finally { setLoading(false); }
  };

  useEffect(() => { load(); }, [id]);

  const updateProject = async (field, value) => {
    const updated = { ...project, [field]: value };
    setProject(updated);
    await api.put(`/projects/${id}`, updated);
  };

  const handleSaveTask = async (form) => {
    try {
      if (editTask?.id) { await api.put(`/tasks/${editTask.id}`, { ...form, project_id: id }); toast.success('Task updated'); }
      else { await api.post('/tasks', { ...form, project_id: id }); toast.success('Task added'); }
      setEditTask(null); setShowNewTask(false); load();
    } catch (e) { toast.error(e.message); }
  };

  const handleSaveNote = async (form) => {
    try { await api.post('/notes', form); toast.success('Note saved'); setShowNewNote(false); load(); }
    catch (e) { toast.error(e.message); }
  };

  const handleDeleteTask = async () => {
    await api.delete(`/tasks/${deleteTask.id}`); toast.success('Task deleted');
    setDeleteTask(null); load();
  };

  const handleDeleteNote = async () => {
    await api.delete(`/notes/${deleteNote.id}`); toast.success('Note deleted');
    setDeleteNote(null); load();
  };

  const toggleFocus = async (task) => { await api.patch(`/tasks/${task.id}/focus`); load(); };

  const openLinkTask = async () => {
    const all = await api.get('/tasks');
    setAllTasks(all.filter(t => t.project_id != id && t.status !== 'Done'));
    setLinkSearch(''); setShowLinkTask(true);
  };

  const linkTask = async (task) => {
    await api.put(`/tasks/${task.id}`, { ...task, project_id: id });
    toast.success(`Linked to project`);
    setAllTasks(prev => prev.filter(t => t.id !== task.id));
    load();
  };

  if (loading) return (

  );

  if (!project) return null;

  const openTasks  = tasks.filter(t => t.status !== 'Done').length;
  const doneTasks  = tasks.filter(t => t.status === 'Done').length;
  const StatusIcon = STATUS_META[project.status]?.icon || Clock;
  const statusColor = STATUS_META[project.status]?.color || 'text-gray-500';
  const statusBg    = STATUS_META[project.status]?.bg    || 'bg-gray-100';

  return (

      {/* Colored accent bar */}
      <div className={`h-1.5 w-full bg-gradient-to-r shrink-0 ${CATEGORY_ACCENT[project.category] || 'from-gray-400 to-gray-500'}`} />

      {/* Header */}
      <div className={`shrink-0 px-5 pt-4 pb-5 ${CATEGORY_BG[project.category] || 'bg-white dark:bg-gray-900'} border-b border-black/5 dark:border-white/5`}>
        {/* Back + actions */}

          <button onClick={() => navigate('/projects')}
            className="flex items-center gap-1.5 text-sm text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors font-medium">
            <ArrowLeft size={15} /> Projects
          </button>
          <button onClick={() => load()}
            className="flex items-center gap-1.5 text-xs text-gray-400 hover:text-gray-600 transition-colors">
            <RefreshCw size={12} />
          </button>

        {/* Category + title */}

          <div className={`w-10 h-10 rounded-xl flex items-center justify-center shrink-0 bg-white/70 dark:bg-gray-800/70 shadow-sm`}>
            <FolderKanban size={18} className={getCategoryClass(project.category).split(' ').find(c => c.startsWith('text-')) || 'text-gray-500'} />

              <InlineEdit value={project.name} onSave={v => updateProject('name', v)} placeholder="Project name" />

        {/* Status / Priority / Category pills */}

          {/* Status selector */}
          <div className={`flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold ${statusBg} ${statusColor}`}>
            <StatusIcon size={11} />
            <select
              value={project.status}
              onChange={e => updateProject('status', e.target.value)}
              className="bg-transparent border-0 outline-none cursor-pointer text-xs font-semibold appearance-none pr-1"
              style={{ color: 'inherit' }}
            >
              {STATUSES.map(s => <option key={s}>{s}</option>)}
            </select>

          {/* Priority selector */}
          <div className={`flex items-center px-3 py-1.5 rounded-full text-xs font-semibold ${getPriorityClass(project.priority)}`}>
            <select
              value={project.priority}
              onChange={e => updateProject('priority', e.target.value)}
              className="bg-transparent border-0 outline-none cursor-pointer text-xs font-semibold appearance-none"
              style={{ color: 'inherit' }}
            >
              {PRIORITIES.map(p => <option key={p}>{p}</option>)}
            </select>

          {/* Category selector */}
          <div className={`flex items-center px-3 py-1.5 rounded-full text-xs font-semibold ${getCategoryClass(project.category)}`}>
            <select
              value={project.category}
              onChange={e => updateProject('category', e.target.value)}
              className="bg-transparent border-0 outline-none cursor-pointer text-xs font-semibold appearance-none"
              style={{ color: 'inherit' }}
            >
              {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </select>

          {/* Task count chip */}

            <CheckSquare size={11} />
            {openTasks} open · {doneTasks} done

      {/* Body — split layout */}

        {/* ── LEFT: Project info ──────────────────────────────────── */}

            <InfoRow icon={AlignLeft} label="Description">
              <InlineEdit
                value={project.description}
                onSave={v => updateProject('description', v)}
                multiline
                placeholder="Add a description…"
                className="leading-relaxed"
              />
            </InfoRow>

            <InfoRow icon={RefreshCw} label="Last Update">
              <InlineEdit
                value={project.last_update}
                onSave={v => updateProject('last_update', v)}
                placeholder="What happened last?"
              />
            </InfoRow>

            <InfoRow icon={ChevronRight} label="Next Step">
              <InlineEdit
                value={project.next_step}
                onSave={v => updateProject('next_step', v)}
                placeholder="What needs to happen next?"
              />
            </InfoRow>

            {/* Stats */}

              {[
                { label: 'Open',  value: openTasks,        color: 'text-blue-600',  bg: 'bg-blue-50 dark:bg-blue-900/20'  },
                { label: 'Done',  value: doneTasks,        color: 'text-green-600', bg: 'bg-green-50 dark:bg-green-900/20'},
                { label: 'Notes', value: notes.length,     color: 'text-violet-600',bg: 'bg-violet-50 dark:bg-violet-900/20'},
              ].map(({ label, value, color, bg }) => (
                <div key={label} className={`${bg} rounded-xl p-3 text-center`}>
                  <p className={`text-xl font-bold ${color}`}>{value}

{label}

              ))}

            {project.updated_at && (

                <Calendar size={9} /> Updated {formatDate(project.updated_at)}

            )}

        {/* ── RIGHT: Tasks / Notes ────────────────────────────────── */}

          {/* Tab bar */}

            {[
              { id: 'tasks', icon: CheckSquare, label: 'Tasks', count: tasks.filter(t => t.status !== 'Done').length },
              { id: 'notes', icon: FileText,    label: 'Notes', count: notes.length },
            ].map(({ id: tid, icon: Icon, label, count }) => (
              <button key={tid} onClick={() => setTab(tid)}
                className={`flex items-center gap-1.5 px-3.5 py-1.5 rounded-xl text-sm font-medium transition-all ${
                  tab === tid
                    ? 'bg-primary text-white shadow-sm'
                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-white'
                }`}>
                <Icon size={13} />{label}
                <span className={`text-[10px] px-1.5 py-0.5 rounded-full font-bold ${tab === tid ? 'bg-white/20' : 'bg-gray-100 dark:bg-gray-800 text-gray-500'}`}>
                  {count}

              </button>
            ))}

            {tab === 'tasks' && (

                <button onClick={openLinkTask} className="btn-secondary flex items-center gap-1 py-1.5 text-xs">
                  <Link2 size={12} /> Link
                </button>
                <button onClick={() => setShowNewTask(true)} className="btn-primary flex items-center gap-1 py-1.5 text-xs">
                  <Plus size={12} /> New Task
                </button>

            )}
            {tab === 'notes' && (
              <button onClick={() => setShowNewNote(!showNewNote)} className="btn-primary flex items-center gap-1 py-1.5 text-xs">
                <Plus size={12} /> Add Note
              </button>
            )}

          {/* Tab content */}

            {/* TASKS */}
            {tab === 'tasks' && (
              <>
                {showNewNote && null}
                {tasks.length === 0 ? (

                    <CheckSquare size={40} className="text-gray-200 dark:text-gray-700" />

No tasks yet

                    <button onClick={() => setShowNewTask(true)} className="btn-primary text-sm">Add first task</button>

                ) : (

                    {tasks.map(t => (
                      <TaskCard key={t.id} task={t} onEdit={setEditTask} onDelete={setDeleteTask} onToggleFocus={toggleFocus} />
                    ))}

                )}
              </>
            )}

            {/* NOTES */}
            {tab === 'notes' && (

                {showNewNote && (
                  <NoteForm projectId={id} onSave={handleSaveNote} onCancel={() => setShowNewNote(false)} />
                )}
                {notes.length === 0 && !showNewNote ? (

                    <FileText size={40} className="text-gray-200 dark:text-gray-700" />

No notes yet

                    <button onClick={() => setShowNewNote(true)} className="btn-primary text-sm">Add first note</button>

                ) : (

                    {notes.map(n => (

{n.title}

                          <button onClick={() => setDeleteNote(n)}
                            className="p-1.5 rounded-lg opacity-0 group-hover:opacity-100 hover:bg-red-50 dark:hover:bg-red-900/20 text-gray-400 hover:text-red-500 transition-all shrink-0">
                            <Trash2 size={12} />
                          </button>

                        {n.body &&
{n.body}
}

                          <Calendar size={9} />{formatDate(n.date)}

                    ))}

                )}

            )}

      {/* Modals */}
      {(showNewTask || editTask) && (
        <TaskModal task={editTask} projects={projects} onSave={handleSaveTask} onClose={() => { setShowNewTask(false); setEditTask(null); }} />
      )}
      {deleteTask && <ConfirmDialog title="Delete task?" message={`"${deleteTask.title}"`} onConfirm={handleDeleteTask} onCancel={() => setDeleteTask(null)} />}
      {deleteNote && <ConfirmDialog title="Delete note?" message={`"${deleteNote.title}"`} onConfirm={handleDeleteNote} onCancel={() => setDeleteNote(null)} />}

      {/* Link existing task modal */}
      {showLinkTask && (

 setShowLinkTask(false)}>

 e.stopPropagation()}>

              <Link2 size={16} className="text-primary shrink-0" />

Link Existing Task

              <button onClick={() => setShowLinkTask(false)} className="btn-icon"><X size={16} /></button>

                <Search size={13} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
                <input className="input pl-8 py-2 text-sm w-full" placeholder="Search tasks…"
                  value={linkSearch} onChange={e => setLinkSearch(e.target.value)} autoFocus />

              {allTasks.filter(t =>
                !linkSearch || t.title.toLowerCase().includes(linkSearch.toLowerCase()) ||
                (t.assignee || '').toLowerCase().includes(linkSearch.toLowerCase())
              ).map(task => (
                <button key={task.id} onClick={() => linkTask(task)}
                  className="flex items-center gap-3 w-full px-5 py-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors text-left group">

{task.title}

                      {task.status}
                      {task.assignee && · {task.assignee}}

                    <Link2 size={11} /> Link

                </button>
              ))}
              {allTasks.filter(t => !linkSearch || t.title.toLowerCase().includes(linkSearch.toLowerCase())).length === 0 && (

No unlinked tasks found

              )}

      )}

  );
}

import { useState, useEffect } from 'react';

import { useParams, useNavigate } from 'react-router-dom';

-import { ArrowLeft, Pencil, Check, Plus, Trash2, FileText, CheckSquare, Link2, Search, X } from 'lucide-react';

+  ArrowLeft, Pencil, Plus, Trash2, FileText, CheckSquare,

+  Link2, Search, X, CheckCircle2, Clock, Loader2, PauseCircle,

+  FolderKanban, AlignLeft, RefreshCw, ChevronRight, Calendar, User

import ConfirmDialog from './ConfirmDialog';

-import { CATEGORIES, STATUSES, PRIORITIES, getCategoryClass, getPriorityClass, getStatusClass } from '../utils';

+import { CATEGORIES, STATUSES, PRIORITIES, getCategoryClass, getPriorityClass, getStatusClass, formatDate } from '../utils';

-function InlineEdit({ value, onSave, multiline = false, placeholder = '' }) {

+  ai:        'from-blue-500 to-cyan-500',

+  vendor:    'from-purple-500 to-violet-500',

+  gov:       'from-amber-500 to-yellow-500',

+  people:    'from-teal-500 to-emerald-500',

+  events:    'from-orange-500 to-red-400',

+  strategic: 'from-green-500 to-teal-500',

+  adhoc:     'from-red-500 to-rose-500',

+  ai:        'bg-blue-50 dark:bg-blue-900/20',

+  vendor:    'bg-purple-50 dark:bg-purple-900/20',

+  gov:       'bg-amber-50 dark:bg-amber-900/20',

+  people:    'bg-teal-50 dark:bg-teal-900/20',

+  events:    'bg-orange-50 dark:bg-orange-900/20',

+  strategic: 'bg-green-50 dark:bg-green-900/20',

+  adhoc:     'bg-red-50 dark:bg-red-900/20',

+  Open:          { icon: Clock,        color: 'text-gray-500',  bg: 'bg-gray-100 dark:bg-gray-800'         },

+  'In Progress': { icon: Loader2,      color: 'text-blue-600',  bg: 'bg-blue-100 dark:bg-blue-900/30'      },

+  Waiting:       { icon: PauseCircle,  color: 'text-amber-600', bg: 'bg-amber-100 dark:bg-amber-900/30'    },

+  Done:          { icon: CheckCircle2, color: 'text-green-600', bg: 'bg-green-100 dark:bg-green-900/30'    },

+function InlineEdit({ value, onSave, multiline = false, placeholder = '', className = '' }) {

const [editing, setEditing] = useState(false);

const [val, setVal] = useState(value || '');

useEffect(() => setVal(value || ''), [value]);

const save = () => { setEditing(false); if (val !== value) onSave(val); };

-      className="group cursor-text text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white flex items-center gap-1"

-      <Pencil size={11} className="opacity-0 group-hover:opacity-50 shrink-0" />

+    <span onClick={() => setEditing(true)}

+      className={`group cursor-text flex items-start gap-1.5 ${className}`}>

+      <span className={!val ? 'text-gray-400 italic text-sm' : 'text-gray-700 dark:text-gray-300'}>

+      <Pencil size={11} className="opacity-0 group-hover:opacity-40 shrink-0 mt-0.5 text-gray-400" />

-    ? <textarea autoFocus className="input w-full resize-none" rows={3} value={val} onChange={e => setVal(e.target.value)} onBlur={save} />

-    : <input autoFocus className="input w-full" value={val} onChange={e => setVal(e.target.value)} onBlur={save} onKeyDown={e => e.key === 'Enter' && save()} />;

+    ? <textarea autoFocus className="input w-full resize-none text-sm" rows={4} value={val}

+        onChange={e => setVal(e.target.value)} onBlur={save} />

+    : <input autoFocus className="input w-full text-sm" value={val}

+        onChange={e => setVal(e.target.value)} onBlur={save} onKeyDown={e => e.key === 'Enter' && save()} />;

+function InfoRow({ icon: Icon, label, children }) {

+      <p className="flex items-center gap-1.5 text-[10px] font-bold text-gray-400 uppercase tracking-wider">

function NoteForm({ projectId, onSave, onCancel }) {

await onSave({ title, body, project_id: projectId, date: new Date().toISOString().split('T')[0] });

-    <form onSubmit={submit} className="card border-dashed space-y-2">

-      <input className="input" placeholder="Note title" value={title} onChange={e => setTitle(e.target.value)} autoFocus required />

-      <textarea className="input resize-none" rows={3} placeholder="Note body…" value={body} onChange={e => setBody(e.target.value)} />

+    <form onSubmit={submit} className="bg-white dark:bg-gray-900 rounded-2xl border-2 border-dashed border-primary/30 p-4 space-y-3">

+      <input className="input font-medium" placeholder="Note title…" value={title} onChange={e => setTitle(e.target.value)} autoFocus required />

+      <textarea className="input resize-none text-sm" rows={4} placeholder="Write your note…" value={body} onChange={e => setBody(e.target.value)} />

<div className="flex gap-2 justify-end">

-        <button type="button" onClick={onCancel} className="btn-secondary text-xs py-1 px-3">Cancel</button>

-        <button type="submit" className="btn-primary text-xs py-1 px-3">Save Note</button>

+        <button type="button" onClick={onCancel} className="btn-secondary text-xs py-1.5 px-3">Cancel</button>

+        <button type="submit" className="btn-primary text-xs py-1.5 px-3">Save Note</button>

export default function ProjectDetail() {

-  const [project, setProject] = useState(null);

-  const [tasks, setTasks] = useState([]);

-  const [notes, setNotes] = useState([]);

-  const [projects, setProjects] = useState([]);

-  const [tab, setTab] = useState('tasks');

-  const [loading, setLoading] = useState(true);

-  const [editTask, setEditTask] = useState(null);

+  const [project, setProject]       = useState(null);

+  const [tasks, setTasks]           = useState([]);

+  const [notes, setNotes]           = useState([]);

+  const [projects, setProjects]     = useState([]);

+  const [tab, setTab]               = useState('tasks');

+  const [loading, setLoading]       = useState(true);

+  const [editTask, setEditTask]     = useState(null);

const [deleteTask, setDeleteTask] = useState(null);

const [deleteNote, setDeleteNote] = useState(null);

-  const [showNewTask, setShowNewTask] = useState(false);

-  const [showNewNote, setShowNewNote] = useState(false);

+  const [showNewTask, setShowNewTask]   = useState(false);

+  const [showNewNote, setShowNewNote]   = useState(false);

const [showLinkTask, setShowLinkTask] = useState(false);

-  const [allTasks, setAllTasks] = useState([]);

+  const [allTasks, setAllTasks]     = useState([]);

const [linkSearch, setLinkSearch] = useState('');

const handleSaveNote = async (form) => {

-      await api.post('/notes', form); toast.success('Note saved');

-    } catch (e) { toast.error(e.message); }

+    try { await api.post('/notes', form); toast.success('Note saved'); setShowNewNote(false); load(); }

+    catch (e) { toast.error(e.message); }

setAllTasks(all.filter(t => t.project_id != id && t.status !== 'Done'));

+    setLinkSearch(''); setShowLinkTask(true);

await api.put(`/tasks/${task.id}`, { ...task, project_id: id });

-    toast.success(`"${task.title}" linked to project`);

+    toast.success(`Linked to project`);

setAllTasks(prev => prev.filter(t => t.id !== task.id));

-    <div className="max-w-2xl mx-auto px-4 py-6 space-y-4">

-      <div className="skeleton h-6 w-32 rounded" />

-      <div className="skeleton h-10 w-2/3 rounded-lg" />

-      <div className="skeleton h-40 rounded-lg" />

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

+      <div className="skeleton h-2 w-full" />

+      <div className="p-6 space-y-4 flex-1">

+        <div className="skeleton h-6 w-28 rounded-xl" />

+        <div className="skeleton h-10 w-2/3 rounded-xl" />

+        <div className="grid grid-cols-1 lg:grid-cols-3 gap-4 mt-4">

+          <div className="skeleton h-64 rounded-2xl" />

+          <div className="lg:col-span-2 skeleton h-64 rounded-2xl" />

+  const openTasks  = tasks.filter(t => t.status !== 'Done').length;

+  const doneTasks  = tasks.filter(t => t.status === 'Done').length;

+  const StatusIcon = STATUS_META[project.status]?.icon || Clock;

+  const statusColor = STATUS_META[project.status]?.color || 'text-gray-500';

+  const statusBg    = STATUS_META[project.status]?.bg    || 'bg-gray-100';

-    <div className="max-w-2xl mx-auto px-4 py-6">

-      <button onClick={() => navigate('/projects')} className="flex items-center gap-1.5 text-sm text-gray-500 hover:text-gray-800 dark:hover:text-white mb-4 transition-colors">

-        <ArrowLeft size={15} /> Projects

+    <div className="flex flex-col h-full overflow-hidden bg-[#F0F2F5] dark:bg-gray-950">

-        <div className="flex items-start gap-3 flex-wrap mb-3">

-            className="badge cursor-pointer border-0 outline-none bg-transparent p-0"

-            onChange={e => updateProject('category', e.target.value)}

-            {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}

-            className={`badge cursor-pointer border-0 outline-none bg-transparent p-0 ${getPriorityClass(project.priority)}`}

-            onChange={e => updateProject('priority', e.target.value)}

-            {PRIORITIES.map(p => <option key={p}>{p}</option>)}

-            className={`badge cursor-pointer border-0 outline-none bg-transparent p-0 ${getStatusClass(project.status)}`}

-            onChange={e => updateProject('status', e.target.value)}

-            {STATUSES.map(s => <option key={s}>{s}</option>)}

+      <div className={`h-1.5 w-full bg-gradient-to-r shrink-0 ${CATEGORY_ACCENT[project.category] || 'from-gray-400 to-gray-500'}`} />

+      <div className={`shrink-0 px-5 pt-4 pb-5 ${CATEGORY_BG[project.category] || 'bg-white dark:bg-gray-900'} border-b border-black/5 dark:border-white/5`}>

+        <div className="flex items-center justify-between mb-3">

+          <button onClick={() => navigate('/projects')}

+            className="flex items-center gap-1.5 text-sm text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors font-medium">

+            <ArrowLeft size={15} /> Projects

+          <button onClick={() => load()}

+            className="flex items-center gap-1.5 text-xs text-gray-400 hover:text-gray-600 transition-colors">

-        <h1 className="text-xl font-bold text-gray-900 dark:text-white mb-4">

-          <InlineEdit value={project.name} onSave={v => updateProject('name', v)} placeholder="Project name" />

+        <div className="flex items-start gap-3 mb-3">

+          <div className={`w-10 h-10 rounded-xl flex items-center justify-center shrink-0 bg-white/70 dark:bg-gray-800/70 shadow-sm`}>

+            <FolderKanban size={18} className={getCategoryClass(project.category).split(' ').find(c => c.startsWith('text-')) || 'text-gray-500'} />

+          <div className="flex-1 min-w-0">

+            <h1 className="text-xl font-bold text-gray-900 dark:text-white leading-snug">

+              <InlineEdit value={project.name} onSave={v => updateProject('name', v)} placeholder="Project name" />

-        <div className="space-y-3 text-sm">

-            <InlineEdit value={project.description} onSave={v => updateProject('description', v)} multiline placeholder="Add description…" />

+        {/* Status / Priority / Category pills */}

+        <div className="flex flex-wrap items-center gap-2">

+          <div className={`flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold ${statusBg} ${statusColor}`}>

+              onChange={e => updateProject('status', e.target.value)}

+              className="bg-transparent border-0 outline-none cursor-pointer text-xs font-semibold appearance-none pr-1"

+              style={{ color: 'inherit' }}

+              {STATUSES.map(s => <option key={s}>{s}</option>)}

-            <InlineEdit value={project.last_update} onSave={v => updateProject('last_update', v)} placeholder="What happened last?" />

+          <div className={`flex items-center px-3 py-1.5 rounded-full text-xs font-semibold ${getPriorityClass(project.priority)}`}>

+              onChange={e => updateProject('priority', e.target.value)}

+              className="bg-transparent border-0 outline-none cursor-pointer text-xs font-semibold appearance-none"

+              {PRIORITIES.map(p => <option key={p}>{p}</option>)}

-            <InlineEdit value={project.next_step} onSave={v => updateProject('next_step', v)} placeholder="What needs to happen next?" />

+          <div className={`flex items-center px-3 py-1.5 rounded-full text-xs font-semibold ${getCategoryClass(project.category)}`}>

+              onChange={e => updateProject('category', e.target.value)}

+              {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}

+          <div className="flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold bg-white/70 dark:bg-gray-800/70 text-gray-600 dark:text-gray-300 ml-auto">

+            {openTasks} open · {doneTasks} done

-      <div className="flex gap-1 mb-4 bg-gray-100 dark:bg-gray-800 p-1 rounded-lg">

-          onClick={() => setTab('tasks')}

-          className={`flex-1 flex items-center justify-center gap-1.5 py-2 px-3 rounded-md text-sm font-medium transition-colors ${

-            tab === 'tasks' ? 'bg-white dark:bg-gray-900 text-gray-900 dark:text-white shadow-sm' : 'text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'

-          <CheckSquare size={14} /> Tasks ({tasks.filter(t => t.status !== 'Done').length})

-          onClick={() => setTab('notes')}

-            tab === 'notes' ? 'bg-white dark:bg-gray-900 text-gray-900 dark:text-white shadow-sm' : 'text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'

-          <FileText size={14} /> Notes ({notes.length})

+      <div className="flex-1 overflow-hidden flex flex-col lg:flex-row min-h-0">

-          <div className="flex gap-2 justify-end">

-            <button onClick={openLinkTask} className="btn-secondary flex items-center gap-1.5 text-xs">

-              <Link2 size={13} /> Link Existing

-            <button onClick={() => setShowNewTask(true)} className="btn-secondary flex items-center gap-1.5 text-xs">

-              <Plus size={13} /> New Task

+        {/* ── LEFT: Project info ──────────────────────────────────── */}

+        <div className="lg:w-72 xl:w-80 shrink-0 overflow-y-auto border-b lg:border-b-0 lg:border-r border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900">

+          <div className="p-5 space-y-5">

+            <InfoRow icon={AlignLeft} label="Description">

+                value={project.description}

+                onSave={v => updateProject('description', v)}

+                placeholder="Add a description…"

+                className="leading-relaxed"

+            <div className="border-t border-gray-100 dark:border-gray-800" />

+            <InfoRow icon={RefreshCw} label="Last Update">

+                value={project.last_update}

+                onSave={v => updateProject('last_update', v)}

+                placeholder="What happened last?"

+            <InfoRow icon={ChevronRight} label="Next Step">

+                value={project.next_step}

+                onSave={v => updateProject('next_step', v)}

+                placeholder="What needs to happen next?"

+            <div className="grid grid-cols-3 gap-2">

+                { label: 'Open',  value: openTasks,        color: 'text-blue-600',  bg: 'bg-blue-50 dark:bg-blue-900/20'  },

+                { label: 'Done',  value: doneTasks,        color: 'text-green-600', bg: 'bg-green-50 dark:bg-green-900/20'},

+                { label: 'Notes', value: notes.length,     color: 'text-violet-600',bg: 'bg-violet-50 dark:bg-violet-900/20'},

+              ].map(({ label, value, color, bg }) => (

+                <div key={label} className={`${bg} rounded-xl p-3 text-center`}>

+                  <p className={`text-xl font-bold ${color}`}>{value}

+              <p className="text-[10px] text-gray-400 flex items-center gap-1">

+                <Calendar size={9} /> Updated {formatDate(project.updated_at)}

-            ?
No tasks yet. Add one above.

-                <TaskCard key={t.id} task={t} onEdit={setEditTask} onDelete={setDeleteTask} onToggleFocus={toggleFocus} />

-          <div className="flex justify-end">

-            <button onClick={() => setShowNewNote(!showNewNote)} className="btn-secondary flex items-center gap-1.5 text-xs">

-              <Plus size={13} /> Add Note

+        {/* ── RIGHT: Tasks / Notes ────────────────────────────────── */}

+        <div className="flex-1 flex flex-col min-h-0 overflow-hidden">

+          <div className="shrink-0 flex items-center gap-1 px-4 py-3 bg-white dark:bg-gray-900 border-b border-gray-100 dark:border-gray-800">

+              { id: 'tasks', icon: CheckSquare, label: 'Tasks', count: tasks.filter(t => t.status !== 'Done').length },

+              { id: 'notes', icon: FileText,    label: 'Notes', count: notes.length },

+            ].map(({ id: tid, icon: Icon, label, count }) => (

+              <button key={tid} onClick={() => setTab(tid)}

+                className={`flex items-center gap-1.5 px-3.5 py-1.5 rounded-xl text-sm font-medium transition-all ${

+                    ? 'bg-primary text-white shadow-sm'

+                    : 'text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-800 dark:hover:text-white'

+                <Icon size={13} />{label}

+                <span className={`text-[10px] px-1.5 py-0.5 rounded-full font-bold ${tab === tid ? 'bg-white/20' : 'bg-gray-100 dark:bg-gray-800 text-gray-500'}`}>

+              <div className="flex gap-1.5">

+                <button onClick={openLinkTask} className="btn-secondary flex items-center gap-1 py-1.5 text-xs">

+                  <Link2 size={12} /> Link

+                <button onClick={() => setShowNewTask(true)} className="btn-primary flex items-center gap-1 py-1.5 text-xs">

+                  <Plus size={12} /> New Task

+              <button onClick={() => setShowNewNote(!showNewNote)} className="btn-primary flex items-center gap-1 py-1.5 text-xs">

+                <Plus size={12} /> Add Note

-          {showNewNote && <NoteForm projectId={id} onSave={handleSaveNote} onCancel={() => setShowNewNote(false)} />}

-          {notes.length === 0 && !showNewNote

-                <div key={n.id} className="card group">

-                  <div className="flex items-start justify-between">

-                    <div className="flex-1">

-                      {n.body &&
{n.body}
}

-                    <button onClick={() => setDeleteNote(n)} className="p-1.5 rounded hover:bg-red-50 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all ml-2">

+          <div className="flex-1 overflow-y-auto p-4">

+                {tasks.length === 0 ? (

+                  <div className="flex flex-col items-center justify-center h-full gap-3 py-16">

+                    <CheckSquare size={40} className="text-gray-200 dark:text-gray-700" />

+                    <button onClick={() => setShowNewTask(true)} className="btn-primary text-sm">Add first task</button>

+                  <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-3">

+                      <TaskCard key={t.id} task={t} onEdit={setEditTask} onDelete={setDeleteTask} onToggleFocus={toggleFocus} />

+              <div className="space-y-3">

+                  <NoteForm projectId={id} onSave={handleSaveNote} onCancel={() => setShowNewNote(false)} />

+                {notes.length === 0 && !showNewNote ? (

+                    <FileText size={40} className="text-gray-200 dark:text-gray-700" />

+                    <button onClick={() => setShowNewNote(true)} className="btn-primary text-sm">Add first note</button>

+                      <div key={n.id} className="group bg-white dark:bg-gray-900 rounded-2xl border border-gray-100 dark:border-gray-800 shadow-card hover:shadow-hover transition-all duration-150 p-4">

+                        <div className="flex items-start justify-between gap-2 mb-2">

+                          <button onClick={() => setDeleteNote(n)}

+                            className="p-1.5 rounded-lg opacity-0 group-hover:opacity-100 hover:bg-red-50 dark:hover:bg-red-900/20 text-gray-400 hover:text-red-500 transition-all shrink-0">

+                        {n.body &&
{n.body}
}

+                        <p className="text-[10px] text-gray-400 mt-3 flex items-center gap-1">

+                          <Calendar size={9} />{formatDate(n.date)}

<div className="px-5 py-3 border-b border-gray-100 dark:border-gray-800">

<Search size={13} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />

-                  className="input pl-8 py-2 text-sm w-full"

-                  placeholder="Search tasks…"

-                  onChange={e => setLinkSearch(e.target.value)}

+                <input className="input pl-8 py-2 text-sm w-full" placeholder="Search tasks…"

+                  value={linkSearch} onChange={e => setLinkSearch(e.target.value)} autoFocus />

<div className="flex-1 overflow-y-auto py-2">

!linkSearch || t.title.toLowerCase().includes(linkSearch.toLowerCase()) ||

+                <button key={task.id} onClick={() => linkTask(task)}

+                  className="flex items-center gap-3 w-full px-5 py-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors text-left group">

+                  <div className="flex-1 min-w-0">

+                    <div className="flex items-center gap-1.5 mt-0.5">

+                      {task.assignee && · {task.assignee}}

+                  <span className="shrink-0 text-[11px] text-primary font-medium opacity-0 group-hover:opacity-100 transition-opacity flex items-center gap-1">

+                    <Link2 size={11} /> Link

+              {allTasks.filter(t => !linkSearch || t.title.toLowerCase().includes(linkSearch.toLowerCase())).length === 0 && (

-                  .filter(t => !linkSearch || t.title.toLowerCase().includes(linkSearch.toLowerCase()) || (t.assignee || '').toLowerCase().includes(linkSearch.toLowerCase()))

-                      onClick={() => linkTask(task)}

-                      className="flex items-center gap-3 w-full px-5 py-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors text-left group"

-                      <div className="flex-1 min-w-0">

-                        <div className="flex items-center gap-1.5 mt-0.5">

-                          {task.status}

-                          {task.assignee && · {task.assignee}}

-                      <span className="shrink-0 text-[11px] text-primary font-medium opacity-0 group-hover:opacity-100 transition-opacity flex items-center gap-1">

-                        <Link2 size={11} /> Link

import { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { ArrowLeft, Pencil, Check, Plus, Trash2, FileText, CheckSquare, Link2, Search, X } from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import TaskCard from './TaskCard';
import TaskModal from './TaskModal';
import ConfirmDialog from './ConfirmDialog';
import { CATEGORIES, STATUSES, PRIORITIES, getCategoryClass, getPriorityClass, getStatusClass } from '../utils';

function InlineEdit({ value, onSave, multiline = false, placeholder = '' }) {
  const [editing, setEditing] = useState(false);
  const [val, setVal] = useState(value || '');
  useEffect(() => setVal(value || ''), [value]);
  const save = () => { setEditing(false); if (val !== value) onSave(val); };
  if (!editing) return (
     setEditing(true)}
      className="group cursor-text text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white flex items-center gap-1"
    >
      {val || placeholder}
      <Pencil size={11} className="opacity-0 group-hover:opacity-50 shrink-0" />

  );
  return multiline
    ? <textarea autoFocus className="input w-full resize-none" rows={3} value={val} onChange={e => setVal(e.target.value)} onBlur={save} />
    : <input autoFocus className="input w-full" value={val} onChange={e => setVal(e.target.value)} onBlur={save} onKeyDown={e => e.key === 'Enter' && save()} />;
}

function NoteForm({ projectId, onSave, onCancel }) {
  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');
  const submit = async (e) => {
    e.preventDefault();
    if (!title.trim()) return;
    await onSave({ title, body, project_id: projectId, date: new Date().toISOString().split('T')[0] });
    setTitle(''); setBody('');
  };
  return (
    <form onSubmit={submit} className="card border-dashed space-y-2">
      <input className="input" placeholder="Note title" value={title} onChange={e => setTitle(e.target.value)} autoFocus required />
      <textarea className="input resize-none" rows={3} placeholder="Note body…" value={body} onChange={e => setBody(e.target.value)} />

        <button type="button" onClick={onCancel} className="btn-secondary text-xs py-1 px-3">Cancel</button>
        <button type="submit" className="btn-primary text-xs py-1 px-3">Save Note</button>

    </form>
  );
}

export default function ProjectDetail() {
  const { id } = useParams();
  const navigate = useNavigate();
  const [project, setProject] = useState(null);
  const [tasks, setTasks] = useState([]);
  const [notes, setNotes] = useState([]);
  const [projects, setProjects] = useState([]);
  const [tab, setTab] = useState('tasks');
  const [loading, setLoading] = useState(true);
  const [editTask, setEditTask] = useState(null);
  const [deleteTask, setDeleteTask] = useState(null);
  const [deleteNote, setDeleteNote] = useState(null);
  const [showNewTask, setShowNewTask] = useState(false);
  const [showNewNote, setShowNewNote] = useState(false);
  const [showLinkTask, setShowLinkTask] = useState(false);
  const [allTasks, setAllTasks] = useState([]);
  const [linkSearch, setLinkSearch] = useState('');

  const load = async () => {
    try {
      const [p, t, n, pr] = await Promise.all([
        api.get(`/projects/${id}`),
        api.get(`/tasks?project_id=${id}`),
        api.get(`/notes?project_id=${id}`),
        api.get('/projects'),
      ]);
      setProject(p); setTasks(t); setNotes(n); setProjects(pr);
    } catch { navigate('/projects'); }
    finally { setLoading(false); }
  };

  useEffect(() => { load(); }, [id]);

  const updateProject = async (field, value) => {
    const updated = { ...project, [field]: value };
    setProject(updated);
    await api.put(`/projects/${id}`, updated);
  };

  const handleSaveTask = async (form) => {
    try {
      if (editTask?.id) { await api.put(`/tasks/${editTask.id}`, { ...form, project_id: id }); toast.success('Task updated'); }
      else { await api.post('/tasks', { ...form, project_id: id }); toast.success('Task added'); }
      setEditTask(null); setShowNewTask(false); load();
    } catch (e) { toast.error(e.message); }
  };

  const handleSaveNote = async (form) => {
    try {
      await api.post('/notes', form); toast.success('Note saved');
      setShowNewNote(false); load();
    } catch (e) { toast.error(e.message); }
  };

  const handleDeleteTask = async () => {
    await api.delete(`/tasks/${deleteTask.id}`); toast.success('Task deleted');
    setDeleteTask(null); load();
  };

  const handleDeleteNote = async () => {
    await api.delete(`/notes/${deleteNote.id}`); toast.success('Note deleted');
    setDeleteNote(null); load();
  };

  const toggleFocus = async (task) => { await api.patch(`/tasks/${task.id}/focus`); load(); };

  const openLinkTask = async () => {
    const all = await api.get('/tasks');
    setAllTasks(all.filter(t => t.project_id != id && t.status !== 'Done'));
    setLinkSearch('');
    setShowLinkTask(true);
  };

  const linkTask = async (task) => {
    await api.put(`/tasks/${task.id}`, { ...task, project_id: id });
    toast.success(`"${task.title}" linked to project`);
    setAllTasks(prev => prev.filter(t => t.id !== task.id));
    load();
  };

  if (loading) return (

  );

  if (!project) return null;

  return (

      {/* Back */}
      <button onClick={() => navigate('/projects')} className="flex items-center gap-1.5 text-sm text-gray-500 hover:text-gray-800 dark:hover:text-white mb-4 transition-colors">
        <ArrowLeft size={15} /> Projects
      </button>

      {/* Project header */}

          <select
            className="badge cursor-pointer border-0 outline-none bg-transparent p-0"
            style={{ minWidth: 0 }}
            value={project.category}
            onChange={e => updateProject('category', e.target.value)}
          >
            {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
          </select>
          <select
            className={`badge cursor-pointer border-0 outline-none bg-transparent p-0 ${getPriorityClass(project.priority)}`}
            value={project.priority}
            onChange={e => updateProject('priority', e.target.value)}
          >
            {PRIORITIES.map(p => <option key={p}>{p}</option>)}
          </select>
          <select
            className={`badge cursor-pointer border-0 outline-none bg-transparent p-0 ${getStatusClass(project.status)}`}
            value={project.status}
            onChange={e => updateProject('status', e.target.value)}
          >
            {STATUSES.map(s => <option key={s}>{s}</option>)}
          </select>

          <InlineEdit value={project.name} onSave={v => updateProject('name', v)} placeholder="Project name" />

Description

            <InlineEdit value={project.description} onSave={v => updateProject('description', v)} multiline placeholder="Add description…" />

Last Update

            <InlineEdit value={project.last_update} onSave={v => updateProject('last_update', v)} placeholder="What happened last?" />

Next Step

            <InlineEdit value={project.next_step} onSave={v => updateProject('next_step', v)} placeholder="What needs to happen next?" />

      {/* Tabs */}

        <button
          onClick={() => setTab('tasks')}
          className={`flex-1 flex items-center justify-center gap-1.5 py-2 px-3 rounded-md text-sm font-medium transition-colors ${
            tab === 'tasks' ? 'bg-white dark:bg-gray-900 text-gray-900 dark:text-white shadow-sm' : 'text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'
          }`}
        >
          <CheckSquare size={14} /> Tasks ({tasks.filter(t => t.status !== 'Done').length})
        </button>
        <button
          onClick={() => setTab('notes')}
          className={`flex-1 flex items-center justify-center gap-1.5 py-2 px-3 rounded-md text-sm font-medium transition-colors ${
            tab === 'notes' ? 'bg-white dark:bg-gray-900 text-gray-900 dark:text-white shadow-sm' : 'text-gray-500 hover:text-gray-700 dark:hover:text-gray-300'
          }`}
        >
          <FileText size={14} /> Notes ({notes.length})
        </button>

      {/* Tab content */}
      {tab === 'tasks' && (

            <button onClick={openLinkTask} className="btn-secondary flex items-center gap-1.5 text-xs">
              <Link2 size={13} /> Link Existing
            </button>
            <button onClick={() => setShowNewTask(true)} className="btn-secondary flex items-center gap-1.5 text-xs">
              <Plus size={13} /> New Task
            </button>

          {tasks.length === 0
            ?
No tasks yet. Add one above.

            : tasks.map(t => (
                <TaskCard key={t.id} task={t} onEdit={setEditTask} onDelete={setDeleteTask} onToggleFocus={toggleFocus} />
              ))
          }

      )}

      {tab === 'notes' && (

            <button onClick={() => setShowNewNote(!showNewNote)} className="btn-secondary flex items-center gap-1.5 text-xs">
              <Plus size={13} /> Add Note
            </button>

          {showNewNote && <NoteForm projectId={id} onSave={handleSaveNote} onCancel={() => setShowNewNote(false)} />}
          {notes.length === 0 && !showNewNote
            ?
No notes yet.

            : notes.map(n => (

{n.title}

{n.date}

                      {n.body &&
{n.body}
}

                    <button onClick={() => setDeleteNote(n)} className="p-1.5 rounded hover:bg-red-50 text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all ml-2">
                      <Trash2 size={13} />
                    </button>

              ))
          }

      )}

      {/* Modals */}
      {(showNewTask || editTask) && (
        <TaskModal task={editTask} projects={projects} onSave={handleSaveTask} onClose={() => { setShowNewTask(false); setEditTask(null); }} />
      )}
      {deleteTask && <ConfirmDialog title="Delete task?" message={`"${deleteTask.title}"`} onConfirm={handleDeleteTask} onCancel={() => setDeleteTask(null)} />}
      {deleteNote && <ConfirmDialog title="Delete note?" message={`"${deleteNote.title}"`} onConfirm={handleDeleteNote} onCancel={() => setDeleteNote(null)} />}

      {/* Link existing task modal */}
      {showLinkTask && (

 setShowLinkTask(false)}>

 e.stopPropagation()}>

              <Link2 size={16} className="text-primary shrink-0" />

Link Existing Task

              <button onClick={() => setShowLinkTask(false)} className="btn-icon"><X size={16} /></button>

                <Search size={13} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
                <input
                  className="input pl-8 py-2 text-sm w-full"
                  placeholder="Search tasks…"
                  value={linkSearch}
                  onChange={e => setLinkSearch(e.target.value)}
                  autoFocus
                />

              {allTasks.filter(t =>
                !linkSearch || t.title.toLowerCase().includes(linkSearch.toLowerCase()) ||
                (t.assignee || '').toLowerCase().includes(linkSearch.toLowerCase())
              ).length === 0 ? (

No unlinked tasks found

              ) : (
                allTasks
                  .filter(t => !linkSearch || t.title.toLowerCase().includes(linkSearch.toLowerCase()) || (t.assignee || '').toLowerCase().includes(linkSearch.toLowerCase()))
                  .map(task => (
                    <button
                      key={task.id}
                      onClick={() => linkTask(task)}
                      className="flex items-center gap-3 w-full px-5 py-3 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors text-left group"
                    >

{task.title}

                          {task.status}
                          {task.assignee && · {task.assignee}}

                        <Link2 size={11} /> Link

                    </button>
                  ))
              )}

      )}

  );
}


Here's what the redesigned project detail page now looks like:

**Header**
- Colored gradient accent bar at the top (matches the project's category color)
- Soft category-tinted background behind the header
- Large editable project name with a folder icon
- Status, Priority, Category as styled pill dropdowns — click to change inline, no ugly plain selects

**Left panel** (project info sidebar)
- Description, Last Update, Next Step as clearly labelled inline-editable fields
- 3 stat tiles: Open tasks / Done tasks / Notes count
- Last updated date at the bottom

**Right panel** (tasks & notes)
- Tabs with counters: **Tasks** and **Notes**, styled like the rest of the app
- **New Task** and **Link** buttons inline in the tab bar
- Tasks in a responsive grid (1 → 2 → 3 columns)
- Notes as proper cards with hover-reveal delete
- Empty states with action buttons when nothing exists yet


- The history tasks, please add retrive button to bring it back to the original place
- Quick note to be on top with different color, bigger a bet, add Voice option, and attachement option, so i can attach and the agent reads the attachemnt captured and start add it as a project or normal task


Let me read the current files before making changes:


1	const express = require('express');
2	const router = express.Router();
3	const Anthropic = require('@anthropic-ai/sdk');
4
5	const SYSTEM_PROMPT = `You are a workspace assistant for a Senior Manager at DEWA (Dubai Electricity and Water Authority).
6	Extract structured data from natural language input and return ONLY valid JSON.
7
8	Categories: ai, vendor, gov, people, events, strategic, adhoc
9	People at DEWA: Chandini, Hamid, Wafa, Ayesha, Shabeer, Hadi, Abdulla, Ahmed, Yasa, Sunil, Umme, Nixon, Rakish, Ummehani, Saif, Santhosh, Abida, Marwan, Moaza, Meera AlKitbi
10
11	Return this exact structure:
12	{
13	  "type": "task" | "note" | "project",
14	  "title": "concise action-oriented title",
15	  "details": "full context preserved",
16	  "category": "category id",
17	  "priority": "Urgent" | "High" | "Medium" | "Low",
18	  "assignee": "person name or empty string",
19	  "due": "YYYY-MM-DD or empty string",
20	  "isFollowUp": true | false,
21	  "projectHint": "most likely related project name or empty string",
22	  "suggestedTodayFocus": true | false
23	}`;
24
25	// POST /api/ai/parse
26	router.post('/parse', async (req, res) => {
27	  const { text } = req.body;
28	  if (!text || !text.trim()) return res.status(400).json({ error: 'text is required' });
29
30	  if (!process.env.ANTHROPIC_API_KEY) {
31	    return res.status(503).json({ error: 'ANTHROPIC_API_KEY not configured' });
32	  }
33
34	  try {
35	    const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
36	    const message = await client.messages.create({
37	      model: 'claude-sonnet-4-6',
38	      max_tokens: 1024,
39	      system: SYSTEM_PROMPT,
40	      messages: [{ role: 'user', content: text }]
41	    });
42
43	    const raw = message.content[0].text.trim();
44	    // Strip markdown code fences if present
45	    const jsonText = raw.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, '');
46	    const parsed = JSON.parse(jsonText);
47	    res.json(parsed);
48	  } catch (err) {
49	    console.error('AI parse error:', err.message);
50	    if (err instanceof SyntaxError) {
51	      return res.status(422).json({ error: 'AI returned invalid JSON', raw: err.message });
52	    }
53	    res.status(500).json({ error: err.message });
54	  }
55	});
56
57	module.exports = router;
58

const express = require('express');
const router = express.Router();
const Anthropic = require('@anthropic-ai/sdk');

const SYSTEM_PROMPT = `You are a workspace assistant for a Senior Manager at DEWA (Dubai Electricity and Water Authority).
Extract structured data from natural language input and return ONLY valid JSON.

Categories: ai, vendor, gov, people, events, strategic, adhoc
People at DEWA: Chandini, Hamid, Wafa, Ayesha, Shabeer, Hadi, Abdulla, Ahmed, Yasa, Sunil, Umme, Nixon, Rakish, Ummehani, Saif, Santhosh, Abida, Marwan, Moaza, Meera AlKitbi

Return this exact structure:
{
  "type": "task" | "note" | "project",
  "title": "concise action-oriented title",
  "details": "full context preserved",
  "category": "category id",
  "priority": "Urgent" | "High" | "Medium" | "Low",
  "assignee": "person name or empty string",
  "due": "YYYY-MM-DD or empty string",
  "isFollowUp": true | false,
  "projectHint": "most likely related project name or empty string",
  "suggestedTodayFocus": true | false
}`;

// POST /api/ai/parse
router.post('/parse', async (req, res) => {
  const { text } = req.body;
  if (!text || !text.trim()) return res.status(400).json({ error: 'text is required' });

  if (!process.env.ANTHROPIC_API_KEY) {
    return res.status(503).json({ error: 'ANTHROPIC_API_KEY not configured' });
  }

  try {
    const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
    const message = await client.messages.create({
      model: 'claude-sonnet-4-6',
      max_tokens: 1024,
      system: SYSTEM_PROMPT,
      messages: [{ role: 'user', content: text }]
    });

    const raw = message.content[0].text.trim();
    // Strip markdown code fences if present
    const jsonText = raw.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, '');
    const parsed = JSON.parse(jsonText);
    res.json(parsed);
  } catch (err) {
    console.error('AI parse error:', err.message);
    if (err instanceof SyntaxError) {
      return res.status(422).json({ error: 'AI returned invalid JSON', raw: err.message });
    }
    res.status(500).json({ error: err.message });
  }
});

module.exports = router;


1	import { useState, useEffect, useRef } from 'react';
2	import {
3	  Sparkles, Save, Trash2, Lightbulb,
4	  CheckSquare, FileText, FolderKanban, Clock,
5	  User, Calendar, Zap, ArrowRight
6	} from 'lucide-react';
7	import toast from 'react-hot-toast';
8	import api from '../api';
9	import { CATEGORIES, PRIORITIES, STATUSES, getCategoryClass, getPriorityClass } from '../utils';
10
11	const HISTORY_KEY = 'quicknote_history';
12	function loadHistory() { try { return JSON.parse(localStorage.getItem(HISTORY_KEY) || '[]'); } catch { return []; } }
13	function pushHistory(item) {
14	  const h = loadHistory();
15	  h.unshift({ ...item, savedAt: new Date().toISOString() });
16	  localStorage.setItem(HISTORY_KEY, JSON.stringify(h.slice(0, 8)));
17	}
18
19	const TYPE_CONFIG = {
20	  task:    { icon: CheckSquare,  color: 'text-primary',   bg: 'bg-primary/10',   label: 'Task'    },
21	  note:    { icon: FileText,     color: 'text-teal-600',  bg: 'bg-teal-50 dark:bg-teal-900/20',   label: 'Note'    },
22	  project: { icon: FolderKanban, color: 'text-violet-600',bg: 'bg-violet-50 dark:bg-violet-900/20',label: 'Project' },
23	};
24
25	const EXAMPLES = [
26	  'Follow up with Hamid about WEF Lighthouse document, urgent by end of week',
27	  'Benchmark presentation meeting with Shabeer on 13 Apr, high priority',
28	  'Note: Google NDA meeting happened — next step is Legal Affairs review',
29	  'New project: AI Ethics workshop for Champions Program, strategic initiative',
30	];
31
32	// ── Parsed result card ───────────────────────────────────────────────────────
33	function ParsedCard({ data, onChange, projects, onSave, onDiscard }) {
34	  const TypeCfg = TYPE_CONFIG[data.type] || TYPE_CONFIG.task;
35	  const TypeIcon = TypeCfg.icon;
36
37	  return (
38

39	      {/* Card header */}
40

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

44

45
AI Parsed Result

46
Review and edit before saving

47

48	        <select
49	          value={data.type}
50	          onChange={e => onChange('type', e.target.value)}
51	          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"
52	        >
53	          <option value="task">Task</option>
54	          <option value="note">Note</option>
55	          <option value="project">Project</option>
56	        </select>
57

58
59

60	        {/* Title */}

import { useState, useEffect, useRef } from 'react';
import {
  Sparkles, Save, Trash2, Lightbulb,
  CheckSquare, FileText, FolderKanban, Clock,
  User, Calendar, Zap, ArrowRight
} from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import { CATEGORIES, PRIORITIES, STATUSES, 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',
];

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

  return (

      {/* Card header */}

        <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 */}


60	        {/* Title */}
61

62	          Title
63	          <input
64	            className="input font-semibold text-base"
65	            value={data.title}
66	            onChange={e => onChange('title', e.target.value)}
67	            placeholder="Title…"
68	          />
69

70
71	        {/* Category + Priority */}
72

73

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

79

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

85

86
87	        {/* Task-specific fields */}
88	        {data.type === 'task' && (
89

90

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

94

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

98

99	        )}
100
101	        {/* Details */}
102

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

106
107	        {/* Task checkboxes */}
108	        {data.type === 'task' && (
109

110
111	              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.isFollowUp} onChange={e => onChange('isFollowUp', e.target.checked)} />
112	              Follow-up needed
113
114
115	              <input type="checkbox" className="w-4 h-4 rounded accent-primary" checked={!!data.suggestedTodayFocus} onChange={e => onChange('suggestedTodayFocus', e.target.checked)} />
116	              Pin to today
117
118

119	        )}
120
121	        {/* Project hint */}
122	        {data.projectHint && (
123

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

126	              Suggested project: {data.projectHint}
127

128

129	        )}
130

131
132	      {/* Actions */}
133

134	        <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">
135	          <Trash2 size={13} /> Discard
136	        </button>
137

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

142

143	  );
144	}
145
146	// ── History card ─────────────────────────────────────────────────────────────
147	function HistoryItem({ item }) {
148	  const TypeCfg = TYPE_CONFIG[item.type] || TYPE_CONFIG.task;
149	  const TypeIcon = TypeCfg.icon;
150	  const when = new Date(item.savedAt);
151	  const timeStr = when.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });
152
153	  return (
154

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

158

159
{item.title}

160

161	          {item.category && (
162	            <span className={`badge text-[10px] py-0 ${getCategoryClass(item.category)}`}>
163	              {CATEGORIES[item.category]?.label?.split(' ')[0]}
164
165	          )}
166	          {item.priority && (
167	            <span className={`badge text-[10px] py-0 ${getPriorityClass(item.priority)}`}>{item.priority}
168	          )}
169	          {timeStr}
170

171

172

173	  );
174	}
175
176	// ── Main component ────────────────────────────────────────────────────────────
177	export default function QuickNote() {
178	  const [text, setText]             = useState('');
179	  const [processing, setProcessing] = useState(false);
180	  const [parsed, setParsed]         = useState(null);
181	  const [editData, setEditData]     = useState(null);
182	  const [projects, setProjects]     = useState([]);
183	  const [history, setHistory]       = useState(loadHistory);
184	  const [showExamples, setShowEx]   = useState(false);
185	  const textareaRef = useRef(null);
186
187	  useEffect(() => { api.get('/projects').then(setProjects).catch(() => {}); }, []);
188
189	  // Process with AI
190	  const process = async () => {
191	    if (!text.trim()) { toast.error('Type something first'); return; }
192	    setProcessing(true);
193	    try {
194	      const result = await api.post('/ai/parse', { text });
195	      setParsed(result);
196	      setEditData({ ...result });
197	    } catch (e) {
198	      toast.error('AI parsing failed: ' + e.message);
199	    } finally { setProcessing(false); }
200	  };
201
202	  const changeField = (k, v) => setEditData(d => ({ ...d, [k]: v }));
203
204	  // Save to workspace
205	  const save = async () => {
206	    if (!editData) return;
207	    try {
208	      const d = editData;
209	      if (d.type === 'task') {
210	        await api.post('/tasks', {
211	          title: d.title, notes: d.details, category: d.category, priority: d.priority,
212	          assignee: d.assignee || '', due_date: d.due || '',
213	          is_follow_up: d.isFollowUp ? 1 : 0,
214	          is_today_focus: d.suggestedTodayFocus ? 1 : 0,
215	        });
216	        toast.success('Task saved!');
217	      } else if (d.type === 'note') {
218	        await api.post('/notes', { title: d.title, body: d.details });
219	        toast.success('Note saved!');
220	      } else {
221	        await api.post('/projects', { name: d.title, category: d.category, description: d.details, priority: d.priority });
222	        toast.success('Project created!');
223	      }
224	      pushHistory({ type: d.type, title: d.title, category: d.category, priority: d.priority });
225	      setHistory(loadHistory());
226	      setParsed(null); setEditData(null); setText('');
227	      textareaRef.current?.focus();
228	    } catch (e) { toast.error(e.message); }
229	  };
230
231	  const discard = () => { setParsed(null); setEditData(null); };
232
233	  const useExample = (ex) => { setText(ex); setShowEx(false); textareaRef.current?.focus(); };
234
235	  return (
236

237	      {/* Header */}
238

239

240

241	            <Zap size={16} className="text-white" />
242

243

244
Quick Note

245
Speak or type — AI structures it instantly

246

247

248

249
250	      {/* Main content — horizontal split on desktop */}
251

252
253	        {/* ── LEFT: Input + Parsed result ────────────────────────────── */}
254

255
256	          {/* Input card */}
257

258	            {/* Textarea */}
259	            <textarea
260	              ref={textareaRef}
261	              value={text}
262	              onChange={e => setText(e.target.value)}
263	              onKeyDown={e => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') process(); }}
264	              rows={5}
265	              placeholder={"Type anything — task, idea, follow-up, meeting note…\n\nExample: \"Follow up with Hamid on WEF doc, urgent, this week\""}
266	              className="w-full px-5 pt-5 pb-3 text-sm 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"
267	            />
268
269	            {/* Toolbar */}
270

271	              {/* Examples */}
272	              <button
273	                onClick={() => setShowEx(!showExamples)}
274	                className="flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
275	              >
276	                <Lightbulb size={13} />
277	                Examples
278	              </button>
279
280

281
282	              {text.trim() && !processing && !parsed && (
283	                <button onClick={() => { setText(''); discard(); }} className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 px-2 transition-colors">
284	                  Clear
285	                </button>
286	              )}
287
288	              <button
289	                onClick={process}
290	                disabled={!text.trim() || processing || !!parsed}
291	                className="btn-primary flex items-center gap-2 py-2 text-sm disabled:opacity-40"
292	              >
293	                {processing ? (
294	                  <> Parsing…</>
295	                ) : (
296	                  <><Sparkles size={14} /> Process ⌘↵</>
297	                )}
298	              </button>
299

300
301	            {/* Example suggestions */}
302	            {showExamples && (
303

304
Try these

305	                {EXAMPLES.map((ex, i) => (
306	                  <button key={i} onClick={() => useExample(ex)}
307	                    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">
308	                    <ArrowRight size={11} className="shrink-0 text-gray-400 group-hover:text-primary transition-colors" />
309	                    {ex}
310	                  </button>
311	                ))}
312

313	            )}
314

315
316	          {/* Parsed result */}
317	          {editData && (
318	            <ParsedCard
319	              data={editData}
320	              onChange={changeField}
321	              projects={projects}
322	              onSave={save}
323	              onDiscard={discard}
324	            />
325	          )}
326
327	          {/* Empty state when no parsed result */}
328	          {!editData && !processing && (
329

330	              <Sparkles size={32} className="text-gray-300 dark:text-gray-700 mb-3" />
331
Type or speak, then click Process

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

333

334	          )}
335

336
337	        {/* ── RIGHT: History ──────────────────────────────────────────── */}
338

339

340
Recent Captures

341
Last {history.length} items saved via Quick Note

342

343
344	          {history.length === 0 ? (
345

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

348
Your saved items will appear here

349

350	          ) : (
351

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

354	          )}
355
356	          {/* Tip box */}
357

358
Pro tip

359

360	              Press ⌘ Enter to process instantly. Mention a person's name and AI will auto-assign.
361

362

363

364

365

366	  );
367	}
368

{/* Title */}

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

        {/* Category + Priority */}

            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>

        {/* Task-specific fields */}
        {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 */}

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

        {/* Task checkboxes */}
        {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

        )}

        {/* Project hint */}
        {data.projectHint && (

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

              Suggested project: {data.projectHint}

        )}

      {/* Actions */}

        <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 [parsed, setParsed]         = useState(null);
  const [editData, setEditData]     = useState(null);
  const [projects, setProjects]     = useState([]);
  const [history, setHistory]       = useState(loadHistory);
  const [showExamples, setShowEx]   = useState(false);
  const textareaRef = useRef(null);

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

  // Process with AI
  const process = async () => {
    if (!text.trim()) { toast.error('Type something first'); return; }
    setProcessing(true);
    try {
      const result = await api.post('/ai/parse', { text });
      setParsed(result);
      setEditData({ ...result });
    } catch (e) {
      toast.error('AI parsing failed: ' + e.message);
    } finally { setProcessing(false); }
  };

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

  // Save to workspace
  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());
      setParsed(null); setEditData(null); setText('');
      textareaRef.current?.focus();
    } catch (e) { toast.error(e.message); }
  };

  const discard = () => { setParsed(null); setEditData(null); };

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

  return (

      {/* Header */}

            <Zap size={16} className="text-white" />

Quick Note

Speak or type — AI structures it instantly

      {/* Main content — horizontal split on desktop */}

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

          {/* Input card */}

            {/* Textarea */}
            <textarea
              ref={textareaRef}
              value={text}
              onChange={e => setText(e.target.value)}
              onKeyDown={e => { if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') process(); }}
              rows={5}
              placeholder={"Type anything — task, idea, follow-up, meeting note…\n\nExample: \"Follow up with Hamid on WEF doc, urgent, this week\""}
              className="w-full px-5 pt-5 pb-3 text-sm 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"
            />

            {/* Toolbar */}

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

              {text.trim() && !processing && !parsed && (
                <button onClick={() => { setText(''); 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() || processing || !!parsed}
                className="btn-primary flex items-center gap-2 py-2 text-sm disabled:opacity-40"
              >
                {processing ? (
                  <> Parsing…</>
                ) : (
                  <><Sparkles size={14} /> 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 when no parsed result */}
          {!editData && !processing && (

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

Type or speak, 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 */}

Pro tip

              Press ⌘ Enter to process instantly. Mention a person's name and AI will auto-assign.

  );
}


Bash

cat server/package.json | grep -E '"dependencies"|multer|pdf'

Check server dependencies


cat: server/package.json: No such file or directory


Check server dependencies


Error: Exit code 1
cat: /Users/khalidbinkalban/my-workspace/server/package.json: No such file or directory


Check root package.json for dependencies


my-workspace

Senior Manager Personal Workspace — DEWA

concurrently "npm run server" "npm run client"


Now I have everything I need. Let me implement all changes — server file-parse endpoint, History restore button, and full Quick Note redesign:


1	require('dotenv').config();
2	const express = require('express');
3	const cors = require('cors');
4	const path = require('path');
5
6	// Initialize DB (runs migrations + seed)
7	require('./db');
8
9	const app = express();
10	app.use(cors());
11	app.use(express.json());
12
13	// Routes
14	app.use('/api/projects', require('./routes/projects'));
15	app.use('/api/tasks', require('./routes/tasks'));
16	app.use('/api/notes', require('./routes/notes'));
17	app.use('/api/ai', require('./routes/ai'));
18	app.use('/api/people', require('./routes/people'));
19	app.use('/api/search', require('./routes/search'));
20	app.use('/api/weekly-review', require('./routes/weekly'));
21
22	// Serve static build in production
23	if (process.env.NODE_ENV === 'production') {
24	  app.use(express.static(path.join(__dirname, '../dist')));
25	  app.get('*', (req, res) => {

require('dotenv').config();
const express = require('express');
const cors = require('cors');
const path = require('path');

// Initialize DB (runs migrations + seed)
require('./db');

const app = express();
app.use(cors());
app.use(express.json());

// Routes
app.use('/api/projects', require('./routes/projects'));
app.use('/api/tasks', require('./routes/tasks'));
app.use('/api/notes', require('./routes/notes'));
app.use('/api/ai', require('./routes/ai'));
app.use('/api/people', require('./routes/people'));
app.use('/api/search', require('./routes/search'));
app.use('/api/weekly-review', require('./routes/weekly'));

// Serve static build in production
if (process.env.NODE_ENV === 'production') {
  app.use(express.static(path.join(__dirname, '../dist')));
  app.get('*', (req, res) => {


- The history tasks, please add retrive button to bring it back to the original place - Quick note to be on top with different color, bigger a bet, add Voice option, and attachement option, so i can a…


Conversation compacted