  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('');

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 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(); };

  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={() => setShowNewTask(true)} className="btn-secondary flex items-center gap-1.5 text-xs">
              <Plus size={13} /> Add 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)} />}

  );
}

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

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

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


Edit

const toggleFocus = async (task) => { await api.patch(`/tasks/${task.id}/focus`); 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();
  };


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

  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();
  };

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(); };

  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={() => setShowNewTask(true)} className="btn-secondary flex items-center gap-1.5 text-xs">
              <Plus size={13} /> Add 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)} />}

  );
}

+    const all = await api.get('/tasks');

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

+    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));

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

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


Now add the Link Task button and modal to the tasks tab section:


{tab === 'tasks' && (

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

{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>


{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>

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={() => setShowNewTask(true)} className="btn-secondary flex items-center gap-1.5 text-xs">
              <Plus size={13} /> Add 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)} />}

  );
}

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

+          <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

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

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


Now add the Link Task modal before the closing modals section:


      {/* 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)} />}
    </div>
  );
}

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

      )}

  );
}


      {/* 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)} />}
    </div>
  );
}

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

      )}

  );
}

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

  );
}

+        <div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center p-0 sm:p-4" onClick={() => setShowLinkTask(false)}>

+          <div className="absolute inset-0 bg-black/40 backdrop-blur-sm" />

+          <div className="relative bg-white dark:bg-gray-900 w-full sm:max-w-md rounded-t-2xl sm:rounded-2xl shadow-2xl border border-gray-200 dark:border-gray-800 max-h-[80vh] flex flex-col" onClick={e => e.stopPropagation()}>

+            <div className="flex items-center gap-3 px-5 pt-5 pb-4 border-b border-gray-100 dark:border-gray-800">

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

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

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

+              <div className="relative">

+                <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)}

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

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

+                (t.assignee || '').toLowerCase().includes(linkSearch.toLowerCase())

+                  .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


All four changes are done. Here's a summary:

**1. History tab** (`/history`) — New page showing all completed tasks in a card grid. Each card has a green checkmark box, shows title (strikethrough), priority/category badges, completion date, and assignee. Click any card to open the full task details in a modal (editable). Searchable by title/assignee.

**2. Tasks checkbox** — The grid view now renders cards with the checkbox embedded inside (top-left, green box when checked or task is Done). Clicking the checkbox selects for bulk-done; clicking the card opens edit modal.

**3. People → My Team** — Renamed in both Sidebar and BottomNav.

**4. Projects tab** — Added to Sidebar NAV and redesigned to full-height layout (matches all other pages). Inside any project's detail page, a **"Link Existing"** button opens a searchable modal listing all unlinked, non-done tasks — click any to attach it to the project.


Enhance the UI of the boxes/cards of the project tab


1	import { useState, useEffect } from 'react';
2	import { Plus, FolderKanban, ChevronRight } from 'lucide-react';
3	import { Link } from 'react-router-dom';
4	import toast from 'react-hot-toast';
5	import api from '../api';
6	import ConfirmDialog from './ConfirmDialog';
7	import { CATEGORIES, STATUSES, PRIORITIES, getCategoryClass, getPriorityClass, getStatusClass, formatDate } from '../utils';
8
9	const ALL_CATS = [{ id: '', label: 'All' }, ...Object.entries(CATEGORIES).map(([id, v]) => ({ id, label: v.label }))];
10	const ALL_STATUSES = ['All', ...STATUSES.filter(s => s !== 'Done'), 'Done'];
11
12	function ProjectModal({ project, onSave, onClose }) {
13	  const [form, setForm] = useState({
14	    name: '', category: 'ai', status: 'Open', priority: 'Medium',
15	    description: '', last_update: '', next_step: '',
16	    ...project,
17	  });
18	  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
19	  const submit = async (e) => {
20	    e.preventDefault();
21	    onSave(form);
22	  };
23	  return (
24

25

26
 e.stopPropagation()}>
27

28
{project?.id ? 'Edit Project' : 'New Project'}

29

30	        <form onSubmit={submit} className="px-5 py-4 space-y-3">
31

32	            Project Name *
33	            <input className="input" required value={form.name} onChange={e => set('name', e.target.value)} autoFocus />
34

35

36

37	              Category
38	              <select className="select" value={form.category} onChange={e => set('category', e.target.value)}>
39	                {Object.entries(CATEGORIES).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
40	              </select>
41

42

43	              Priority
44	              <select className="select" value={form.priority} onChange={e => set('priority', e.target.value)}>
45	                {PRIORITIES.map(p => <option key={p}>{p}</option>)}
46	              </select>
47

48

49

50	            Status
51	            <select className="select" value={form.status} onChange={e => set('status', e.target.value)}>
52	              {STATUSES.map(s => <option key={s}>{s}</option>)}
53	            </select>
54

55

56	            Description
57	            <textarea className="input resize-none" rows={2} value={form.description} onChange={e => set('description', e.target.value)} />
58

59

60	            <button type="button" onClick={onClose} className="btn-secondary">Cancel</button>
61	            <button type="submit" className="btn-primary">{project?.id ? 'Save' : 'Create Project'}</button>
62

63	        </form>
64

65

66	  );
67	}
68
69	export default function Projects() {
70	  const [projects, setProjects] = useState([]);
71	  const [loading, setLoading] = useState(true);
72	  const [catFilter, setCatFilter] = useState('');
73	  const [statusFilter, setStatusFilter] = useState('All');
74	  const [editProject, setEditProject] = useState(null);
75	  const [deleteProject, setDeleteProject] = useState(null);
76	  const [showNew, setShowNew] = useState(false);
77
78	  const load = async () => {
79	    setLoading(true);
80	    const params = new URLSearchParams();
81	    if (catFilter) params.set('category', catFilter);
82	    if (statusFilter !== 'All') params.set('status', statusFilter);
83	    try {
84	      setProjects(await api.get('/projects?' + params));
85	    } finally { setLoading(false); }
86	  };
87
88	  useEffect(() => { load(); }, [catFilter, statusFilter]);
89
90	  const handleSave = async (form) => {
91	    try {
92	      if (form.id) { await api.put(`/projects/${form.id}`, form); toast.success('Project updated'); }
93	      else { await api.post('/projects', form); toast.success('Project created'); }
94	      setEditProject(null); setShowNew(false); load();
95	    } catch (e) { toast.error(e.message); }
96	  };
97
98	  const handleDelete = async () => {
99	    try {
100	      await api.delete(`/projects/${deleteProject.id}`);
101	      toast.success('Project deleted');
102	      setDeleteProject(null);
103	      load();
104	    } catch (e) { toast.error(e.message); }
105	  };
106
107	  return (
108

109	      {/* Header */}
110

111

112

113
Projects

114	            {!loading &&
{projects.length} project{projects.length !== 1 ? 's' : ''}
}
115

116	          <button onClick={() => setShowNew(true)} className="btn-primary flex items-center gap-1.5 py-2">
117	            <Plus size={14} /> New Project
118	          </button>
119

120
121	        {/* Category pills */}
122

123	          {ALL_CATS.map(c => (
124	            <button
125	              key={c.id}
126	              onClick={() => setCatFilter(c.id)}
127	              className={`shrink-0 px-3 py-1.5 rounded-full text-xs font-medium transition-colors ${
128	                catFilter === c.id
129	                  ? 'bg-primary text-white'
130	                  : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
131	              }`}
132	            >
133	              {c.label}
134	            </button>
135	          ))}
136

137
138	        {/* Status tabs */}
139

140	          {ALL_STATUSES.map(s => (
141	            <button
142	              key={s}
143	              onClick={() => setStatusFilter(s)}
144	              className={`px-3 py-1 rounded-lg text-xs font-medium transition-colors ${
145	                statusFilter === s
146	                  ? 'bg-gray-800 dark:bg-white text-white dark:text-gray-900'
147	                  : 'bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
148	              }`}
149	            >
150	              {s}
151	            </button>
152	          ))}
153

154

155
156	      {/* Grid */}
157

158	        {loading ? (
159

160	            {[...Array(9)].map((_, i) =>
)}
161

162	        ) : projects.length === 0 ? (
163

164	            <FolderKanban size={44} className="text-gray-200 dark:text-gray-700" />
165
No projects found

166	            <button onClick={() => setShowNew(true)} className="btn-primary">Create a project</button>
167

168	        ) : (
169

170	            {projects.map(p => (
171	              <Link to={`/projects/${p.id}`} key={p.id} className="card-hover block group">
172

173	                  <span className={`badge text-[10px] ${getCategoryClass(p.category)}`}>{CATEGORIES[p.category]?.label || p.category}
174	                  <ChevronRight size={14} className="text-gray-400 mt-0.5 opacity-0 group-hover:opacity-100 transition-opacity" />
175

176
{p.name}

177

178	                  <span className={`badge text-[10px] ${getPriorityClass(p.priority)}`}>{p.priority}
179	                  <span className={`badge text-[10px] ${getStatusClass(p.status)}`}>{p.status}
180

181

182	                  {p.open_task_count || 0} open task{p.open_task_count !== 1 ? 's' : ''}
183	                  {p.updated_at && {formatDate(p.updated_at)}}
184

185	              </Link>
186	            ))}
187

188	        )}
189

190
191	      {(showNew || editProject) && (
192	        <ProjectModal
193	          project={editProject}
194	          onSave={handleSave}
195	          onClose={() => { setShowNew(false); setEditProject(null); }}
196	        />
197	      )}
198	      {deleteProject && (
199	        <ConfirmDialog
200	          title="Delete project?"
201	          message={`"${deleteProject.name}" and its associations will be removed.`}
202	          onConfirm={handleDelete}
203	          onCancel={() => setDeleteProject(null)}
204	        />
205	      )}
206

207	  );
208	}
209

import { useState, useEffect } from 'react';
import { Plus, FolderKanban, ChevronRight } from 'lucide-react';
import { Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import api from '../api';
import ConfirmDialog from './ConfirmDialog';
import { CATEGORIES, STATUSES, PRIORITIES, getCategoryClass, getPriorityClass, getStatusClass, formatDate } from '../utils';

const ALL_CATS = [{ id: '', label: 'All' }, ...Object.entries(CATEGORIES).map(([id, v]) => ({ id, label: v.label }))];
const ALL_STATUSES = ['All', ...STATUSES.filter(s => s !== 'Done'), 'Done'];

function ProjectModal({ project, onSave, onClose }) {
  const [form, setForm] = useState({
    name: '', category: 'ai', status: 'Open', priority: 'Medium',
    description: '', last_update: '', next_step: '',
    ...project,
  });
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const submit = async (e) => {
    e.preventDefault();
    onSave(form);
  };
  return (

 e.stopPropagation()}>

{project?.id ? 'Edit Project' : 'New Project'}

        <form onSubmit={submit} className="px-5 py-4 space-y-3">

            Project Name *
            <input className="input" required value={form.name} onChange={e => set('name', e.target.value)} autoFocus />

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

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

            Status
            <select className="select" value={form.status} onChange={e => set('status', e.target.value)}>
              {STATUSES.map(s => <option key={s}>{s}</option>)}
            </select>

            Description
            <textarea className="input resize-none" rows={2} value={form.description} onChange={e => set('description', e.target.value)} />

            <button type="button" onClick={onClose} className="btn-secondary">Cancel</button>
            <button type="submit" className="btn-primary">{project?.id ? 'Save' : 'Create Project'}</button>

        </form>

  );
}

export default function Projects() {
  const [projects, setProjects] = useState([]);
  const [loading, setLoading] = useState(true);
  const [catFilter, setCatFilter] = useState('');
  const [statusFilter, setStatusFilter] = useState('All');
  const [editProject, setEditProject] = useState(null);
  const [deleteProject, setDeleteProject] = useState(null);
  const [showNew, setShowNew] = useState(false);

  const load = async () => {
    setLoading(true);
    const params = new URLSearchParams();
    if (catFilter) params.set('category', catFilter);
    if (statusFilter !== 'All') params.set('status', statusFilter);
    try {
      setProjects(await api.get('/projects?' + params));
    } finally { setLoading(false); }
  };

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

  const handleSave = async (form) => {
    try {
      if (form.id) { await api.put(`/projects/${form.id}`, form); toast.success('Project updated'); }
      else { await api.post('/projects', form); toast.success('Project created'); }
      setEditProject(null); setShowNew(false); load();
    } catch (e) { toast.error(e.message); }
  };

  const handleDelete = async () => {
    try {
      await api.delete(`/projects/${deleteProject.id}`);
      toast.success('Project deleted');
      setDeleteProject(null);
      load();
    } catch (e) { toast.error(e.message); }
  };

  return (

      {/* Header */}

Projects

            {!loading &&
{projects.length} project{projects.length !== 1 ? 's' : ''}
}

          <button onClick={() => setShowNew(true)} className="btn-primary flex items-center gap-1.5 py-2">
            <Plus size={14} /> New Project
          </button>

        {/* Category pills */}

          {ALL_CATS.map(c => (
            <button
              key={c.id}
              onClick={() => setCatFilter(c.id)}
              className={`shrink-0 px-3 py-1.5 rounded-full text-xs font-medium transition-colors ${
                catFilter === c.id
                  ? 'bg-primary text-white'
                  : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
              }`}
            >
              {c.label}
            </button>
          ))}

        {/* Status tabs */}

          {ALL_STATUSES.map(s => (
            <button
              key={s}
              onClick={() => setStatusFilter(s)}
              className={`px-3 py-1 rounded-lg text-xs font-medium transition-colors ${
                statusFilter === s
                  ? 'bg-gray-800 dark:bg-white text-white dark:text-gray-900'
                  : 'bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
              }`}
            >
              {s}
            </button>
          ))}

      {/* Grid */}

        {loading ? (

            {[...Array(9)].map((_, i) =>
)}

        ) : projects.length === 0 ? (

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

No projects found

            <button onClick={() => setShowNew(true)} className="btn-primary">Create a project</button>

        ) : (

            {projects.map(p => (
              <Link to={`/projects/${p.id}`} key={p.id} className="card-hover block group">

                  <span className={`badge text-[10px] ${getCategoryClass(p.category)}`}>{CATEGORIES[p.category]?.label || p.category}
                  <ChevronRight size={14} className="text-gray-400 mt-0.5 opacity-0 group-hover:opacity-100 transition-opacity" />

{p.name}

                  <span className={`badge text-[10px] ${getPriorityClass(p.priority)}`}>{p.priority}
                  <span className={`badge text-[10px] ${getStatusClass(p.status)}`}>{p.status}

                  {p.open_task_count || 0} open task{p.open_task_count !== 1 ? 's' : ''}
                  {p.updated_at && {formatDate(p.updated_at)}}

              </Link>
            ))}

        )}

      {(showNew || editProject) && (
        <ProjectModal
          project={editProject}
          onSave={handleSave}
          onClose={() => { setShowNew(false); setEditProject(null); }}
        />
      )}
      {deleteProject && (
        <ConfirmDialog
          title="Delete project?"
          message={`"${deleteProject.name}" and its associations will be removed.`}
          onConfirm={handleDelete}
          onCancel={() => setDeleteProject(null)}
        />
      )}

  );
}


Let me check the utils and CSS to understand the current color system:


1:export const CATEGORIES = {
14:export const getCategoryClass = (cat) => ({
24:export const getPriorityClass = (p) => ({


1	export const CATEGORIES = {
2	  ai:       { label: 'AI Adoption & Strategy',  color: 'blue'   },
3	  vendor:   { label: 'Vendor & Partnerships',   color: 'purple' },
4	  gov:      { label: 'Governance & Reporting',  color: 'amber'  },
5	  people:   { label: 'People & Team',           color: 'teal'   },
6	  events:   { label: 'Events & External',       color: 'orange' },
7	  strategic:{ label: 'Strategic Initiatives',   color: 'green'  },
8	  adhoc:    { label: 'CAIO / EVP Ad Hoc',       color: 'red'    },
9	};
10
11	export const STATUSES = ['Open', 'In Progress', 'Waiting', 'Done'];
12	export const PRIORITIES = ['Urgent', 'High', 'Medium', 'Low'];
13
14	export const getCategoryClass = (cat) => ({
15	  ai:        'bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300',
16	  vendor:    'bg-purple-100 text-purple-700 dark:bg-purple-900/50 dark:text-purple-300',
17	  gov:       'bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300',
18	  people:    'bg-teal-100 text-teal-700 dark:bg-teal-900/50 dark:text-teal-300',
19	  events:    'bg-orange-100 text-orange-700 dark:bg-orange-900/50 dark:text-orange-300',
20	  strategic: 'bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300',
21	  adhoc:     'bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300',
22	}[cat] || 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400');
23
24	export const getPriorityClass = (p) => ({
25	  Urgent: 'bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300',
26	  High:   'bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300',
27	  Medium: 'bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300',
28	  Low:    'bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300',
29	}[p] || 'bg-gray-100 text-gray-600');
30
31	export const getStatusClass = (s) => ({
32	  Open:        'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400',
33	  'In Progress':'bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300',
34	  Waiting:     'bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300',
35	  Done:        'bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300',
36	}[s] || 'bg-gray-100 text-gray-600');
37
38	export function isOverdue(dueDate) {
39	  if (!dueDate) return false;
40	  return new Date(dueDate) < new Date(new Date().toDateString());
41	}
42
43	export function formatDate(d) {
44	  if (!d) return '';
45	  const date = new Date(d);
46	  return date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
47	}
48
49	export function daysAgo(dateStr) {
50	  if (!dateStr) return 0;

export const CATEGORIES = {
  ai:       { label: 'AI Adoption & Strategy',  color: 'blue'   },
  vendor:   { label: 'Vendor & Partnerships',   color: 'purple' },
  gov:      { label: 'Governance & Reporting',  color: 'amber'  },
  people:   { label: 'People & Team',           color: 'teal'   },
  events:   { label: 'Events & External',       color: 'orange' },
  strategic:{ label: 'Strategic Initiatives',   color: 'green'  },
  adhoc:    { label: 'CAIO / EVP Ad Hoc',       color: 'red'    },
};

export const STATUSES = ['Open', 'In Progress', 'Waiting', 'Done'];
export const PRIORITIES = ['Urgent', 'High', 'Medium', 'Low'];

export const getCategoryClass = (cat) => ({
  ai:        'bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300',
  vendor:    'bg-purple-100 text-purple-700 dark:bg-purple-900/50 dark:text-purple-300',
  gov:       'bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300',
  people:    'bg-teal-100 text-teal-700 dark:bg-teal-900/50 dark:text-teal-300',
  events:    'bg-orange-100 text-orange-700 dark:bg-orange-900/50 dark:text-orange-300',
  strategic: 'bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300',
  adhoc:     'bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300',
}[cat] || 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400');

export const getPriorityClass = (p) => ({
  Urgent: 'bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-300',
  High:   'bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300',
  Medium: 'bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300',
  Low:    'bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300',
}[p] || 'bg-gray-100 text-gray-600');

export const getStatusClass = (s) => ({
  Open:        'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400',
  'In Progress':'bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300',
  Waiting:     'bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300',
  Done:        'bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-300',
}[s] || 'bg-gray-100 text-gray-600');

export function isOverdue(dueDate) {
  if (!dueDate) return false;
  return new Date(dueDate) < new Date(new Date().toDateString());
}

export function formatDate(d) {
  if (!d) return '';
  const date = new Date(d);
  return date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
}

export function daysAgo(dateStr) {
  if (!dateStr) return 0;


Now I have everything I need. I'll redesign the project cards with colored accents, category icons, description previews, task counts, and polished hover effects:


import { useState, useEffect } from 'react';
import { Plus, FolderKanban, ChevronRight } from 'lucide-react';
import { Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import api from '../api';
import ConfirmDialog from './ConfirmDialog';
import { CATEGORIES, STATUSES, PRIORITIES, getCategoryClass, getPriorityClass, getStatusClass, formatDate } from '../utils';

const ALL_CATS = [{ id: '', label: 'All' }, ...Object.entries(CATEGORIES).map(([id, v]) => ({ id, label: v.label }))];
const ALL_STATUSES = ['All', ...STATUSES.filter(s => s !== 'Done'), 'Done'];

import { useState, useEffect } from 'react';
import { Plus, FolderKanban, ChevronRight, CheckSquare, Pencil, Trash2, CheckCircle2, Clock, Loader2, PauseCircle } from 'lucide-react';
import { Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import api from '../api';
import ConfirmDialog from './ConfirmDialog';
import { CATEGORIES, STATUSES, PRIORITIES, getCategoryClass, getPriorityClass, getStatusClass, formatDate } from '../utils';

const ALL_CATS = [{ id: '', label: 'All' }, ...Object.entries(CATEGORIES).map(([id, v]) => ({ id, label: v.label }))];
const ALL_STATUSES = ['All', ...STATUSES.filter(s => s !== 'Done'), 'Done'];

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_ICON_BG = {
  ai:        'bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400',
  vendor:    'bg-purple-50 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400',
  gov:       'bg-amber-50 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400',
  people:    'bg-teal-50 dark:bg-teal-900/30 text-teal-600 dark:text-teal-400',
  events:    'bg-orange-50 dark:bg-orange-900/30 text-orange-600 dark:text-orange-400',
  strategic: 'bg-green-50 dark:bg-green-900/30 text-green-600 dark:text-green-400',
  adhoc:     'bg-red-50 dark:bg-red-900/30 text-red-600 dark:text-red-400',
};

const STATUS_ICON = {
  Open:         { icon: Clock,        cls: 'text-gray-400'  },
  'In Progress':{ icon: Loader2,      cls: 'text-blue-500'  },
  Waiting:      { icon: PauseCircle,  cls: 'text-amber-500' },
  Done:         { icon: CheckCircle2, cls: 'text-green-500' },
};


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

import { useState, useEffect } from 'react';
import { Plus, FolderKanban, ChevronRight, CheckSquare, Pencil, Trash2, CheckCircle2, Clock, Loader2, PauseCircle } from 'lucide-react';
import { Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import api from '../api';
import ConfirmDialog from './ConfirmDialog';
import { CATEGORIES, STATUSES, PRIORITIES, getCategoryClass, getPriorityClass, getStatusClass, formatDate } from '../utils';

const ALL_CATS = [{ id: '', label: 'All' }, ...Object.entries(CATEGORIES).map(([id, v]) => ({ id, label: v.label }))];
const ALL_STATUSES = ['All', ...STATUSES.filter(s => s !== 'Done'), 'Done'];

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_ICON_BG = {
  ai:        'bg-blue-50 dark:bg-blue-900/30 text-blue-600 dark:text-blue-400',
  vendor:    'bg-purple-50 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400',
  gov:       'bg-amber-50 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400',
  people:    'bg-teal-50 dark:bg-teal-900/30 text-teal-600 dark:text-teal-400',
  events:    'bg-orange-50 dark:bg-orange-900/30 text-orange-600 dark:text-orange-400',
  strategic: 'bg-green-50 dark:bg-green-900/30 text-green-600 dark:text-green-400',
  adhoc:     'bg-red-50 dark:bg-red-900/30 text-red-600 dark:text-red-400',
};

const STATUS_ICON = {
  Open:         { icon: Clock,        cls: 'text-gray-400'  },
  'In Progress':{ icon: Loader2,      cls: 'text-blue-500'  },
  Waiting:      { icon: PauseCircle,  cls: 'text-amber-500' },
  Done:         { icon: CheckCircle2, cls: 'text-green-500' },
};

import { useState, useEffect } from 'react';
import { Plus, FolderKanban, ChevronRight } from 'lucide-react';
import { Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import api from '../api';
import ConfirmDialog from './ConfirmDialog';
import { CATEGORIES, STATUSES, PRIORITIES, getCategoryClass, getPriorityClass, getStatusClass, formatDate } from '../utils';

const ALL_CATS = [{ id: '', label: 'All' }, ...Object.entries(CATEGORIES).map(([id, v]) => ({ id, label: v.label }))];
const ALL_STATUSES = ['All', ...STATUSES.filter(s => s !== 'Done'), 'Done'];

function ProjectModal({ project, onSave, onClose }) {
  const [form, setForm] = useState({
    name: '', category: 'ai', status: 'Open', priority: 'Medium',
    description: '', last_update: '', next_step: '',
    ...project,
  });
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const submit = async (e) => {
    e.preventDefault();
    onSave(form);
  };
  return (

 e.stopPropagation()}>

{project?.id ? 'Edit Project' : 'New Project'}

        <form onSubmit={submit} className="px-5 py-4 space-y-3">

            Project Name *
            <input className="input" required value={form.name} onChange={e => set('name', e.target.value)} autoFocus />

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

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

            Status
            <select className="select" value={form.status} onChange={e => set('status', e.target.value)}>
              {STATUSES.map(s => <option key={s}>{s}</option>)}
            </select>

            Description
            <textarea className="input resize-none" rows={2} value={form.description} onChange={e => set('description', e.target.value)} />

            <button type="button" onClick={onClose} className="btn-secondary">Cancel</button>
            <button type="submit" className="btn-primary">{project?.id ? 'Save' : 'Create Project'}</button>

        </form>

  );
}

export default function Projects() {
  const [projects, setProjects] = useState([]);
  const [loading, setLoading] = useState(true);
  const [catFilter, setCatFilter] = useState('');
  const [statusFilter, setStatusFilter] = useState('All');
  const [editProject, setEditProject] = useState(null);
  const [deleteProject, setDeleteProject] = useState(null);
  const [showNew, setShowNew] = useState(false);

  const load = async () => {
    setLoading(true);
    const params = new URLSearchParams();
    if (catFilter) params.set('category', catFilter);
    if (statusFilter !== 'All') params.set('status', statusFilter);
    try {
      setProjects(await api.get('/projects?' + params));
    } finally { setLoading(false); }
  };

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

  const handleSave = async (form) => {
    try {
      if (form.id) { await api.put(`/projects/${form.id}`, form); toast.success('Project updated'); }
      else { await api.post('/projects', form); toast.success('Project created'); }
      setEditProject(null); setShowNew(false); load();
    } catch (e) { toast.error(e.message); }
  };

  const handleDelete = async () => {
    try {
      await api.delete(`/projects/${deleteProject.id}`);
      toast.success('Project deleted');
      setDeleteProject(null);
      load();
    } catch (e) { toast.error(e.message); }
  };

  return (

      {/* Header */}

Projects

            {!loading &&
{projects.length} project{projects.length !== 1 ? 's' : ''}
}

          <button onClick={() => setShowNew(true)} className="btn-primary flex items-center gap-1.5 py-2">
            <Plus size={14} /> New Project
          </button>

        {/* Category pills */}

          {ALL_CATS.map(c => (
            <button
              key={c.id}
              onClick={() => setCatFilter(c.id)}
              className={`shrink-0 px-3 py-1.5 rounded-full text-xs font-medium transition-colors ${
                catFilter === c.id
                  ? 'bg-primary text-white'
                  : 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
              }`}
            >
              {c.label}
            </button>
          ))}

        {/* Status tabs */}

          {ALL_STATUSES.map(s => (
            <button
              key={s}
              onClick={() => setStatusFilter(s)}
              className={`px-3 py-1 rounded-lg text-xs font-medium transition-colors ${
                statusFilter === s
                  ? 'bg-gray-800 dark:bg-white text-white dark:text-gray-900'
                  : 'bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700'
              }`}
            >
              {s}
            </button>
          ))}

      {/* Grid */}

        {loading ? (

            {[...Array(9)].map((_, i) =>
)}

        ) : projects.length === 0 ? (

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

No projects found

            <button onClick={() => setShowNew(true)} className="btn-primary">Create a project</button>

        ) : (

            {projects.map(p => (
              <Link to={`/projects/${p.id}`} key={p.id} className="card-hover block group">

                  <span className={`badge text-[10px] ${getCategoryClass(p.category)}`}>{CATEGORIES[p.category]?.label || p.category}
                  <ChevronRight size={14} className="text-gray-400 mt-0.5 opacity-0 group-hover:opacity-100 transition-opacity" />

{p.name}

                  <span className={`badge text-[10px] ${getPriorityClass(p.priority)}`}>{p.priority}
                  <span className={`badge text-[10px] ${getStatusClass(p.status)}`}>{p.status}

                  {p.open_task_count || 0} open task{p.open_task_count !== 1 ? 's' : ''}
                  {p.updated_at && {formatDate(p.updated_at)}}

              </Link>
            ))}

        )}

      {(showNew || editProject) && (
        <ProjectModal
          project={editProject}
          onSave={handleSave}
          onClose={() => { setShowNew(false); setEditProject(null); }}
        />
      )}
      {deleteProject && (
        <ConfirmDialog
          title="Delete project?"
          message={`"${deleteProject.name}" and its associations will be removed.`}
          onConfirm={handleDelete}
          onCancel={() => setDeleteProject(null)}
        />
      )}

  );
}

-import { Plus, FolderKanban, ChevronRight } from 'lucide-react';

+import { Plus, FolderKanban, ChevronRight, CheckSquare, Pencil, Trash2, CheckCircle2, Clock, Loader2, PauseCircle } from 'lucide-react';

+  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/30 text-blue-600 dark:text-blue-400',

+  vendor:    'bg-purple-50 dark:bg-purple-900/30 text-purple-600 dark:text-purple-400',

+  gov:       'bg-amber-50 dark:bg-amber-900/30 text-amber-600 dark:text-amber-400',

+  people:    'bg-teal-50 dark:bg-teal-900/30 text-teal-600 dark:text-teal-400',

+  events:    'bg-orange-50 dark:bg-orange-900/30 text-orange-600 dark:text-orange-400',

+  strategic: 'bg-green-50 dark:bg-green-900/30 text-green-600 dark:text-green-400',

+  adhoc:     'bg-red-50 dark:bg-red-900/30 text-red-600 dark:text-red-400',

+  Open:         { icon: Clock,        cls: 'text-gray-400'  },

+  'In Progress':{ icon: Loader2,      cls: 'text-blue-500'  },

+  Waiting:      { icon: PauseCircle,  cls: 'text-amber-500' },

+  Done:         { icon: CheckCircle2, cls: 'text-green-500' },


Now replace the card rendering:


            {projects.map(p => (
              <Link to={`/projects/${p.id}`} key={p.id} className="card-hover block group">

                  <span className={`badge text-[10px] ${getCategoryClass(p.category)}`}>{CATEGORIES[p.category]?.label || p.category}
                  <ChevronRight size={14} className="text-gray-400 mt-0.5 opacity-0 group-hover:opacity-100 transition-opacity" />

{p.name}

                  <span className={`badge text-[10px] ${getPriorityClass(p.priority)}`}>{p.priority}
                  <span className={`badge text-[10px] ${getStatusClass(p.status)}`}>{p.status}

                  {p.open_task_count || 0} open task{p.open_task_count !== 1 ? 's' : ''}
                  {p.updated_at && {formatDate(p.updated_at)}}

              </Link>
            ))}

            {projects.map(p => {
              const StatusIcon = STATUS_ICON[p.status]?.icon || Clock;
              const statusCls  = STATUS_ICON[p.status]?.cls  || 'text-gray-400';
              const isDone     = p.status === 'Done';
              return (
                <Link
                  to={`/projects/${p.id}`}
                  key={p.id}
                  className={`group relative bg-white dark:bg-gray-900 rounded-2xl border border-gray-100 dark:border-gray-800 shadow-card hover:shadow-hover hover:-translate-y-1 transition-all duration-200 overflow-hidden flex flex-col ${isDone ? 'opacity-70' : ''}`}
                >
                  {/* Colored top accent bar */}
                  <div className={`h-1.5 w-full bg-gradient-to-r ${CATEGORY_ACCENT[p.category] || 'from-gray-400 to-gray-500'}`} />

                    {/* Icon + category + arrow */}

                      <div className={`w-9 h-9 rounded-xl flex items-center justify-center shrink-0 ${CATEGORY_ICON_BG[p.category] || 'bg-gray-100 text-gray-500'}`}>
                        <FolderKanban size={16} />

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

                      <ChevronRight size={13} className="text-gray-300 dark:text-gray-600 shrink-0 mt-1 group-hover:text-primary group-hover:translate-x-0.5 transition-all duration-150" />

                    {/* Title */}

                      {p.name}

                    {/* Description */}
                    {p.description ? (

                        {p.description}

                    ) :
}

                    {/* Priority */}

                      <span className={`badge text-[10px] py-0.5 ${getPriorityClass(p.priority)}`}>{p.priority}

                    {/* Footer */}

                      {/* Status with icon */}

                        <StatusIcon size={11} className={statusCls} />
                        {p.status}

                      {/* Task count */}

                        <CheckSquare size={10} className="text-gray-400" />
                        {p.open_task_count || 0}

                  {/* Edit / Delete actions on hover */}

                    <button
                      onClick={e => { e.preventDefault(); setEditProject(p); }}
                      className="w-6 h-6 rounded-lg bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center text-gray-400 hover:text-primary hover:border-primary shadow-sm transition-all"
                    >
                      <Pencil size={10} />
                    </button>
                    <button
                      onClick={e => { e.preventDefault(); setDeleteProject(p); }}
                      className="w-6 h-6 rounded-lg bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center text-gray-400 hover:text-red-500 hover:border-red-300 shadow-sm transition-all"
                    >
                      <Trash2 size={10} />
                    </button>

                </Link>
              );
            })}