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

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' },
};

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

  );
}

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

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

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

-                  <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" />

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

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

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

-                <div className="flex items-center justify-between text-[10px] text-gray-400">

-                  <span>{p.open_task_count || 0} open task{p.open_task_count !== 1 ? 's' : ''}</span>

-                  {p.updated_at && <span>{formatDate(p.updated_at)}</span>}

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

+              const StatusIcon = STATUS_ICON[p.status]?.icon || Clock;

+              const statusCls  = STATUS_ICON[p.status]?.cls  || 'text-gray-400';

+              const isDone     = p.status === 'Done';

+                  to={`/projects/${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'}`} />

+                  <div className="p-4 flex flex-col flex-1">

+                    {/* Icon + category + arrow */}

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

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

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

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

+                    <h3 className="font-bold text-sm text-gray-900 dark:text-white leading-snug line-clamp-2 mb-1.5">

+                    {/* Description */}

+                      <p className="text-xs text-gray-500 dark:text-gray-400 line-clamp-2 leading-relaxed mb-3">

+                        {p.description}

+                    ) : <div className="mb-3" />}

+                    <div className="mt-auto">

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

+                    <div className="flex items-center justify-between mt-3 pt-2.5 border-t border-gray-100 dark:border-gray-800">

+                      {/* Status with icon */}

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

+                        <StatusIcon size={11} className={statusCls} />

+                      {/* Task count */}

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

+                        <CheckSquare size={10} className="text-gray-400" />

+                        {p.open_task_count || 0}

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

+                  <div className="absolute top-3 right-3 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity z-10">

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

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


Also update the skeleton loaders to match the new taller card height:


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' },
};

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 => {
              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>
              );
            })}

        )}

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

  );
}

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

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

-            {[...Array(9)].map((_, i) => <div key={i} className="skeleton h-32 rounded-xl" />)}

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

+            {[...Array(9)].map((_, i) => <div key={i} className="skeleton h-48 rounded-2xl" />)}

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


Each project card now has:

- **Colored gradient accent bar** at the top — unique per category (blue for AI, purple for Vendor, amber for Governance, teal for People, etc.)
- **Category icon** in a matching soft-tinted rounded square
- **Category badge** with color
- **Bold project title** (2-line clamp)
- **Description preview** (2 lines) when available
- **Priority badge** with color coding
- **Footer row** — status with a contextual icon (Clock/Loader/Pause/CheckCircle) + open task count
- **Edit & Delete buttons** that fade in on hover (top-right corner), so you can act without navigating in
- **Done projects** are slightly dimmed (opacity-70)
- **Hover** lifts the card with a stronger shadow and the arrow slides right


for my team I want to have several coloumn to be filled with information
Name/PR number/Grade/Designation/Department/Section/Reporting line/Mobile Number/Place
'/Users/khalidbinkalban/Downloads/CAIO Batch 3 - AlSheraa Shift.pdf' add the names highlighted in yellow only


Read

/Users/khalidbinkalban/Downloads/CAIO Batch 3 - AlSheraa Shift.pdf


PDF file read: /Users/khalidbinkalban/Downloads/CAIO Batch 3 - AlSheraa Shift.pdf (179.1KB)


Let me read the server files to understand the current DB schema and people routes:


1	const Database = require('better-sqlite3');
2	const path = require('path');
3	const fs = require('fs');
4
5	const DB_PATH = path.join(__dirname, '../data/workspace.db');
6	const dataDir = path.dirname(DB_PATH);
7	if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });
8
9	const db = new Database(DB_PATH);
10	db.pragma('journal_mode = WAL');
11	db.pragma('foreign_keys = ON');
12
13	function initDb() {
14	  db.exec(`
15	    CREATE TABLE IF NOT EXISTS projects (
16	      id INTEGER PRIMARY KEY AUTOINCREMENT,
17	      name TEXT NOT NULL,
18	      category TEXT NOT NULL DEFAULT 'adhoc',
19	      status TEXT NOT NULL DEFAULT 'Open',
20	      priority TEXT NOT NULL DEFAULT 'Medium',
21	      description TEXT DEFAULT '',
22	      last_update TEXT DEFAULT '',
23	      next_step TEXT DEFAULT '',
24	      created_at TEXT DEFAULT (datetime('now')),
25	      updated_at TEXT DEFAULT (datetime('now'))
26	    );
27
28	    CREATE TABLE IF NOT EXISTS tasks (
29	      id INTEGER PRIMARY KEY AUTOINCREMENT,
30	      title TEXT NOT NULL,
31	      notes TEXT DEFAULT '',
32	      project_id INTEGER REFERENCES projects(id) ON DELETE SET NULL,
33	      category TEXT NOT NULL DEFAULT 'adhoc',
34	      priority TEXT NOT NULL DEFAULT 'Medium',
35	      status TEXT NOT NULL DEFAULT 'Open',
36	      assignee TEXT DEFAULT '',
37	      due_date TEXT DEFAULT '',
38	      is_follow_up INTEGER DEFAULT 0,
39	      is_today_focus INTEGER DEFAULT 0,
40	      completed_at TEXT DEFAULT '',
41	      created_at TEXT DEFAULT (datetime('now')),
42	      updated_at TEXT DEFAULT (datetime('now'))
43	    );
44
45	    CREATE TABLE IF NOT EXISTS notes (
46	      id INTEGER PRIMARY KEY AUTOINCREMENT,
47	      title TEXT NOT NULL,
48	      body TEXT DEFAULT '',
49	      project_id INTEGER REFERENCES projects(id) ON DELETE SET NULL,
50	      date TEXT DEFAULT (date('now')),
51	      created_at TEXT DEFAULT (datetime('now'))
52	    );
53
54	    CREATE TABLE IF NOT EXISTS people (
55	      id INTEGER PRIMARY KEY AUTOINCREMENT,
56	      name TEXT NOT NULL UNIQUE,
57	      role TEXT DEFAULT '',
58	      created_at TEXT DEFAULT (datetime('now'))
59	    );
60
61	    CREATE TABLE IF NOT EXISTS settings (
62	      key TEXT PRIMARY KEY,
63	      value TEXT
64	    );
65
66	    CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
67	    CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id);
68	    CREATE INDEX IF NOT EXISTS idx_tasks_today ON tasks(is_today_focus);
69	    CREATE INDEX IF NOT EXISTS idx_tasks_followup ON tasks(is_follow_up);
70	    CREATE INDEX IF NOT EXISTS idx_projects_category ON projects(category);
71	    CREATE INDEX IF NOT EXISTS idx_notes_date ON notes(date);
72	  `);
73
74	  const seeded = db.prepare("SELECT value FROM settings WHERE key = 'seeded'").get();
75	  if (!seeded) {
76	    seedData();
77	    db.prepare("INSERT INTO settings (key, value) VALUES ('seeded', 'true')").run();
78	    console.log('✅ Database seeded with initial data');
79	  }
80	}
81
82	function seedData() {
83	  const insertProject = db.prepare(`
84	    INSERT INTO projects (name, category, status, priority) VALUES (?, ?, ?, ?)
85	  `);
86
87	  const projects = [
88	    // AI Adoption & Strategy
89	    ['Microsoft Copilot Rollout', 'ai', 'In Progress', 'High'],
90	    ['DEWA AI Academy', 'ai', 'In Progress', 'High'],
91	    ['AI Command Center RFP', 'ai', 'Open', 'High'],
92	    ['AI Champions Program', 'ai', 'In Progress', 'High'],
93	    ['AI OS Initiative', 'ai', 'In Progress', 'Urgent'],
94	    ['COE End-to-End', 'ai', 'In Progress', 'High'],
95	    ['GitHub Copilot Plan', 'ai', 'Open', 'High'],
96	    ['WalkMe & SAP Joule', 'ai', 'In Progress', 'Medium'],
97	    ['Rummana at Work', 'ai', 'Open', 'Medium'],
98	    ['Blink App AI Adoption', 'ai', 'Open', 'Medium'],
99	    // Vendor & Partnerships
100	    ['Google Partnership & NDA', 'vendor', 'Waiting', 'Urgent'],
101	    ['Polynome Engagement', 'vendor', 'In Progress', 'High'],
102	    ['Genspark Engagement', 'vendor', 'Waiting', 'Medium'],
103	    ['IMD Proposal', 'vendor', 'Open', 'Medium'],
104	    ['Ducont Wave 2', 'vendor', 'In Progress', 'High'],
105	    ['ElevenLabs Engagement', 'vendor', 'Open', 'Medium'],
106	    ['Dell Engagement', 'vendor', 'Open', 'Medium'],
107	    ['Coursera / Udemy Licenses', 'vendor', 'In Progress', 'Medium'],
108	    // Governance & Reporting
109	    ['I&TF Quarterly Report', 'gov', 'In Progress', 'Urgent'],
110	    ['AI Charter & ISO 42001', 'gov', 'Open', 'High'],
111	    ['KPIs & Blueprint', 'gov', 'In Progress', 'High'],
112	    ['IETF Benchmark', 'gov', 'In Progress', 'High'],
113	    // People & Team
114	    ['Employees Rating', 'people', 'Open', 'High'],
115	    ['Data Champions Program', 'people', 'Open', 'Medium'],
116	    ['Outsource Assessment', 'people', 'In Progress', 'Medium'],
117	    ['AI Ambassadors Survey', 'people', 'In Progress', 'High'],
118	    // Events & External
119	    ['WEF Lighthouse', 'events', 'Open', 'High'],
120	    ['Gartner Data Summit', 'events', 'Open', 'Medium'],
121	    ['GenAI Week Emirates Towers', 'events', 'Open', 'Medium'],
122	    ['Microsoft Community Conference', 'events', 'Open', 'Medium'],
123	    // Strategic Initiatives
124	    ['AlHasbah Use Cases Phase I', 'strategic', 'In Progress', 'High'],
125	    ['Digital DEWA Direction', 'strategic', 'Open', 'High'],
126	    ['D2D Workshop', 'strategic', 'In Progress', 'High'],
127	    ['AICC ROI Tracking', 'strategic', 'Open', 'High'],
128	    // CAIO / EVP Ad Hoc
129	    ['EVP MD Finance Agent', 'adhoc', 'Open', 'Urgent'],
130	    ['CAIO AI OS Initiative', 'adhoc', 'In Progress', 'High'],
131	  ];
132
133	  const seedProjects = db.transaction(() => {
134	    for (const p of projects) insertProject.run(...p);
135	  });
136	  seedProjects();
137
138	  const insertTask = db.prepare(`
139	    INSERT INTO tasks (title, category, priority, assignee, due_date, is_follow_up, status)
140	    VALUES (?, ?, ?, ?, ?, ?, 'Open')
141	  `);
142
143	  const tasks = [
144	    ['Google NDA: meet Legal Affairs + Hadi + CAIO', 'vendor', 'Urgent', 'Hadi', '', 1],
145	    ['Chandini to provide process map of features announcement', 'ai', 'High', 'Chandini', '', 1],
146	    ['Copilot ROI analysis', 'ai', 'High', 'Self', '', 0],
147	    ['AlHasbah KPIs review with Shabeer', 'gov', 'High', 'Shabeer', '', 1],
148	    ['I&TF Quarterly report — Wafa, Ayesha, Umme, Nixon, Chandini', 'gov', 'Urgent', '', '2026-03-27', 0],
149	    ['EVP asked for MD Finance Agent', 'adhoc', 'Urgent', '', '', 0],
150	    ['WEF Lighthouse document — shared with Hamid and Ahmad AlShehhi', 'events', 'High', 'Hamid', '', 1],
151	    ['Check Udemy license distribution across divisions', 'ai', 'Medium', '', '', 0],
152	    ['GitHub Copilot end-to-end plan', 'ai', 'High', '', '', 0],
153	    ['Benchmark presentation meeting on 13 Apr', 'gov', 'High', 'Hamid', '2026-04-13', 1],
154	    ['Polynome NDA to be signed', 'vendor', 'High', '', '', 1],
155	    ['AI Ethics & Governance workshop for AI Champions', 'ai', 'High', '', '', 0],
156	    ['Copilot Studio Kit demo with Yasa', 'ai', 'High', 'Yasa', '', 0],
157	    ['Check with Microsoft to connect Viva Learning team', 'vendor', 'Medium', '', '', 0],
158	    ['D2D issue still open', 'strategic', 'High', '', '', 0],
159	    ['Staff mobile allowance', 'people', 'Medium', '', '', 0],
160	    ['Universities update', 'events', 'Medium', '', '', 0],
161	    ['Follow up on Anthropic contacts', 'vendor', 'High', '', '', 1],
162	    ['Genspark credit issue resolution', 'vendor', 'Medium', '', '', 1],
163	    ['Survey for AI Champions — measure performance', 'ai', 'High', '', '', 0],
164	  ];
165
166	  const seedTasks = db.transaction(() => {
167	    for (const t of tasks) insertTask.run(...t);
168	  });
169	  seedTasks();
170
171	  const insertPerson = db.prepare('INSERT OR IGNORE INTO people (name) VALUES (?)');
172	  const people = [
173	    'Chandini', 'Hamid', 'Wafa', 'Ayesha', 'Shabeer', 'Hadi',
174	    'Abdulla', 'Ahmed', 'Yasa', 'Sunil', 'Umme', 'Nixon',
175	    'Rakish', 'Ummehani', 'Saif', 'Santhosh', 'Abida',
176	    'Marwan', 'Moaza', 'Meera AlKitbi'
177	  ];
178	  const seedPeople = db.transaction(() => {
179	    for (const name of people) insertPerson.run(name);
180	  });
181	  seedPeople();
182	}
183
184	initDb();
185
186	module.exports = db;
187

const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');

const DB_PATH = path.join(__dirname, '../data/workspace.db');
const dataDir = path.dirname(DB_PATH);
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });

const db = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');

function initDb() {
  db.exec(`
    CREATE TABLE IF NOT EXISTS projects (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL,
      category TEXT NOT NULL DEFAULT 'adhoc',
      status TEXT NOT NULL DEFAULT 'Open',
      priority TEXT NOT NULL DEFAULT 'Medium',
      description TEXT DEFAULT '',
      last_update TEXT DEFAULT '',
      next_step TEXT DEFAULT '',
      created_at TEXT DEFAULT (datetime('now')),
      updated_at TEXT DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS tasks (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      title TEXT NOT NULL,
      notes TEXT DEFAULT '',
      project_id INTEGER REFERENCES projects(id) ON DELETE SET NULL,
      category TEXT NOT NULL DEFAULT 'adhoc',
      priority TEXT NOT NULL DEFAULT 'Medium',
      status TEXT NOT NULL DEFAULT 'Open',
      assignee TEXT DEFAULT '',
      due_date TEXT DEFAULT '',
      is_follow_up INTEGER DEFAULT 0,
      is_today_focus INTEGER DEFAULT 0,
      completed_at TEXT DEFAULT '',
      created_at TEXT DEFAULT (datetime('now')),
      updated_at TEXT DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS notes (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      title TEXT NOT NULL,
      body TEXT DEFAULT '',
      project_id INTEGER REFERENCES projects(id) ON DELETE SET NULL,
      date TEXT DEFAULT (date('now')),
      created_at TEXT DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS people (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT NOT NULL UNIQUE,
      role TEXT DEFAULT '',
      created_at TEXT DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS settings (
      key TEXT PRIMARY KEY,
      value TEXT
    );

    CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
    CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id);
    CREATE INDEX IF NOT EXISTS idx_tasks_today ON tasks(is_today_focus);
    CREATE INDEX IF NOT EXISTS idx_tasks_followup ON tasks(is_follow_up);
    CREATE INDEX IF NOT EXISTS idx_projects_category ON projects(category);
    CREATE INDEX IF NOT EXISTS idx_notes_date ON notes(date);
  `);

  const seeded = db.prepare("SELECT value FROM settings WHERE key = 'seeded'").get();
  if (!seeded) {
    seedData();
    db.prepare("INSERT INTO settings (key, value) VALUES ('seeded', 'true')").run();
    console.log('✅ Database seeded with initial data');
  }
}

function seedData() {
  const insertProject = db.prepare(`
    INSERT INTO projects (name, category, status, priority) VALUES (?, ?, ?, ?)
  `);

  const projects = [
    // AI Adoption & Strategy
    ['Microsoft Copilot Rollout', 'ai', 'In Progress', 'High'],
    ['DEWA AI Academy', 'ai', 'In Progress', 'High'],
    ['AI Command Center RFP', 'ai', 'Open', 'High'],
    ['AI Champions Program', 'ai', 'In Progress', 'High'],
    ['AI OS Initiative', 'ai', 'In Progress', 'Urgent'],
    ['COE End-to-End', 'ai', 'In Progress', 'High'],
    ['GitHub Copilot Plan', 'ai', 'Open', 'High'],
    ['WalkMe & SAP Joule', 'ai', 'In Progress', 'Medium'],
    ['Rummana at Work', 'ai', 'Open', 'Medium'],
    ['Blink App AI Adoption', 'ai', 'Open', 'Medium'],
    // Vendor & Partnerships
    ['Google Partnership & NDA', 'vendor', 'Waiting', 'Urgent'],
    ['Polynome Engagement', 'vendor', 'In Progress', 'High'],
    ['Genspark Engagement', 'vendor', 'Waiting', 'Medium'],
    ['IMD Proposal', 'vendor', 'Open', 'Medium'],
    ['Ducont Wave 2', 'vendor', 'In Progress', 'High'],
    ['ElevenLabs Engagement', 'vendor', 'Open', 'Medium'],
    ['Dell Engagement', 'vendor', 'Open', 'Medium'],
    ['Coursera / Udemy Licenses', 'vendor', 'In Progress', 'Medium'],
    // Governance & Reporting
    ['I&TF Quarterly Report', 'gov', 'In Progress', 'Urgent'],
    ['AI Charter & ISO 42001', 'gov', 'Open', 'High'],
    ['KPIs & Blueprint', 'gov', 'In Progress', 'High'],
    ['IETF Benchmark', 'gov', 'In Progress', 'High'],
    // People & Team
    ['Employees Rating', 'people', 'Open', 'High'],
    ['Data Champions Program', 'people', 'Open', 'Medium'],
    ['Outsource Assessment', 'people', 'In Progress', 'Medium'],
    ['AI Ambassadors Survey', 'people', 'In Progress', 'High'],
    // Events & External
    ['WEF Lighthouse', 'events', 'Open', 'High'],
    ['Gartner Data Summit', 'events', 'Open', 'Medium'],
    ['GenAI Week Emirates Towers', 'events', 'Open', 'Medium'],
    ['Microsoft Community Conference', 'events', 'Open', 'Medium'],
    // Strategic Initiatives
    ['AlHasbah Use Cases Phase I', 'strategic', 'In Progress', 'High'],
    ['Digital DEWA Direction', 'strategic', 'Open', 'High'],
    ['D2D Workshop', 'strategic', 'In Progress', 'High'],
    ['AICC ROI Tracking', 'strategic', 'Open', 'High'],
    // CAIO / EVP Ad Hoc
    ['EVP MD Finance Agent', 'adhoc', 'Open', 'Urgent'],
    ['CAIO AI OS Initiative', 'adhoc', 'In Progress', 'High'],
  ];

  const seedProjects = db.transaction(() => {
    for (const p of projects) insertProject.run(...p);
  });
  seedProjects();

  const insertTask = db.prepare(`
    INSERT INTO tasks (title, category, priority, assignee, due_date, is_follow_up, status)
    VALUES (?, ?, ?, ?, ?, ?, 'Open')
  `);

  const tasks = [
    ['Google NDA: meet Legal Affairs + Hadi + CAIO', 'vendor', 'Urgent', 'Hadi', '', 1],
    ['Chandini to provide process map of features announcement', 'ai', 'High', 'Chandini', '', 1],
    ['Copilot ROI analysis', 'ai', 'High', 'Self', '', 0],
    ['AlHasbah KPIs review with Shabeer', 'gov', 'High', 'Shabeer', '', 1],
    ['I&TF Quarterly report — Wafa, Ayesha, Umme, Nixon, Chandini', 'gov', 'Urgent', '', '2026-03-27', 0],
    ['EVP asked for MD Finance Agent', 'adhoc', 'Urgent', '', '', 0],
    ['WEF Lighthouse document — shared with Hamid and Ahmad AlShehhi', 'events', 'High', 'Hamid', '', 1],
    ['Check Udemy license distribution across divisions', 'ai', 'Medium', '', '', 0],
    ['GitHub Copilot end-to-end plan', 'ai', 'High', '', '', 0],
    ['Benchmark presentation meeting on 13 Apr', 'gov', 'High', 'Hamid', '2026-04-13', 1],
    ['Polynome NDA to be signed', 'vendor', 'High', '', '', 1],
    ['AI Ethics & Governance workshop for AI Champions', 'ai', 'High', '', '', 0],
    ['Copilot Studio Kit demo with Yasa', 'ai', 'High', 'Yasa', '', 0],
    ['Check with Microsoft to connect Viva Learning team', 'vendor', 'Medium', '', '', 0],
    ['D2D issue still open', 'strategic', 'High', '', '', 0],
    ['Staff mobile allowance', 'people', 'Medium', '', '', 0],
    ['Universities update', 'events', 'Medium', '', '', 0],
    ['Follow up on Anthropic contacts', 'vendor', 'High', '', '', 1],
    ['Genspark credit issue resolution', 'vendor', 'Medium', '', '', 1],
    ['Survey for AI Champions — measure performance', 'ai', 'High', '', '', 0],
  ];

  const seedTasks = db.transaction(() => {
    for (const t of tasks) insertTask.run(...t);
  });
  seedTasks();

  const insertPerson = db.prepare('INSERT OR IGNORE INTO people (name) VALUES (?)');
  const people = [
    'Chandini', 'Hamid', 'Wafa', 'Ayesha', 'Shabeer', 'Hadi',
    'Abdulla', 'Ahmed', 'Yasa', 'Sunil', 'Umme', 'Nixon',
    'Rakish', 'Ummehani', 'Saif', 'Santhosh', 'Abida',
    'Marwan', 'Moaza', 'Meera AlKitbi'
  ];
  const seedPeople = db.transaction(() => {
    for (const name of people) insertPerson.run(name);
  });
  seedPeople();
}

initDb();

module.exports = db;


Error: EISDIR: illegal operation on a directory, read '/Users/khalidbinkalban/my-workspace/server/routes'


server/routes/tasks.js
server/routes/notes.js
server/routes/ai.js
server/routes/people.js
server/routes/search.js
server/routes/weekly.js
server/routes/projects.js


1	const express = require('express');
2	const router = express.Router();
3	const db = require('../db');
4
5	// GET /api/people
6	router.get('/', (req, res) => {
7	  res.json(db.prepare('SELECT * FROM people ORDER BY name').all());
8	});
9
10	// GET /api/people/:name/tasks
11	router.get('/:name/tasks', (req, res) => {
12	  const tasks = db.prepare(`
13	    SELECT t.*, p.name as project_name
14	    FROM tasks t
15	    LEFT JOIN projects p ON t.project_id = p.id
16	    WHERE t.assignee = ?
17	    ORDER BY CASE t.status WHEN 'Open' THEN 1 WHEN 'In Progress' THEN 2 WHEN 'Waiting' THEN 3 ELSE 4 END, t.created_at DESC
18	  `).all(req.params.name);
19	  res.json(tasks);
20	});
21
22	// POST /api/people
23	router.post('/', (req, res) => {
24	  const { name, role = '' } = req.body;
25	  if (!name) return res.status(400).json({ error: 'name is required' });
26	  try {
27	    const result = db.prepare('INSERT INTO people (name, role) VALUES (?, ?)').run(name, role);
28	    res.json(db.prepare('SELECT * FROM people WHERE id = ?').get(result.lastInsertRowid));
29	  } catch (e) {
30	    res.status(409).json({ error: 'Person already exists' });
31	  }
32	});
33
34	module.exports = router;
35

const express = require('express');
const router = express.Router();
const db = require('../db');

// GET /api/people
router.get('/', (req, res) => {
  res.json(db.prepare('SELECT * FROM people ORDER BY name').all());
});

// GET /api/people/:name/tasks
router.get('/:name/tasks', (req, res) => {
  const tasks = db.prepare(`
    SELECT t.*, p.name as project_name
    FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.assignee = ?
    ORDER BY CASE t.status WHEN 'Open' THEN 1 WHEN 'In Progress' THEN 2 WHEN 'Waiting' THEN 3 ELSE 4 END, t.created_at DESC
  `).all(req.params.name);
  res.json(tasks);
});

// POST /api/people
router.post('/', (req, res) => {
  const { name, role = '' } = req.body;
  if (!name) return res.status(400).json({ error: 'name is required' });
  try {
    const result = db.prepare('INSERT INTO people (name, role) VALUES (?, ?)').run(name, role);
    res.json(db.prepare('SELECT * FROM people WHERE id = ?').get(result.lastInsertRowid));
  } catch (e) {
    res.status(409).json({ error: 'Person already exists' });
  }
});

module.exports = router;


Now I have everything. This needs: DB column migration, updated people routes, team data seed, and a full People.jsx redesign as a directory table. Let me implement it all:


Now I have the full picture. I need to:
1. Migrate the `people` DB table with new columns
2. Seed the highlighted names
3. Add a PUT route for editing
4. Redesign People.jsx as a proper team directory

Let me implement everything:


Agent

Implement My Team directory feature

Prompt:
You need to make changes across 3 files in a Node.js + React + SQLite workspace app. Here are the exact changes needed:

---

## 1. `/Users/khalidbinkalban/my-workspace/server/db.js`

After the `db.pragma('foreign_keys = ON');` line (around line 11) and BEFORE the `function initDb()` declaration, add this migration block:

```js
// Migrate people table — add columns if not present
;['pr_number','grade','designation','department','section','reporting_line','mobile_number','place'].forEach(col => {
  try { db.exec(`ALTER TABLE people ADD COLUMN ${col} TEXT DEFAULT ''`); } catch {}
});
```

Then, inside `function seedData()`, find the people section (around line 171 starting with `const insertPerson = db.prepare('INSERT OR IGNORE INTO people (name) VALUES (?)')`). Replace EVERYTHING from that `const insertPerson` line through the end of `seedData()` (the closing `}`) with this:

```js
  // Seed people from CAIO Batch 3 - AlSheraa Shift (yellow-highlighted)
  const seededPeopleV2 = db.prepare("SELECT value FROM settings WHERE key = 'people_v2'").get();
  if (!seededPeopleV2) {
    db.prepare('DELETE FROM people').run();
    const insertPerson = db.prepare(`
      INSERT OR IGNORE INTO people (name, pr_number, designation)
      VALUES (?, ?, ?)
    `);
    const teamMembers = [
      ['Ayesha Mohd Ali Sarwashi',              '15610',    'Specialist - Digital Services'],
      ['Saleha Obaid Essa',                      '24290',    'Sr Demand Analyst'],
      ['Mohammad Saif Bin Sulaiman',             '29433',    'Sr Engr - Digital Asset Mgmt'],
      ['Eiman Abdulla Alkindi',                  '23463',    'Sr Engr - Projects Govrnce'],
      ['Ahmad Mohamad Bin Dakhan',               '25260',    'Sr Exec - Digital Comm'],
      ['Noora Hassan Abdullatif Alsarah',        '24743',    'Sr Officer - Digital Products Mgmt'],
      ['Ahmed Saleh Ahmed Alshehhi',             '22990',    'Sr Exec - Customer Haps'],
      ['Wafa Mohd Mubarak Albalooshi',           '24911',    'Sr Exec - Admin'],
      ['Ayesha Eissa Humaid Abdulla Alzaabi',    '26971',    'Sr Exec - Admin'],
      ['Shamsa Ahmad Abdulla Alabdulla',         '29509',    'Sr Officer - Portfolio Delivery Mgmt'],
      ['Abdulla Almuhairi',                      '14933',    'Sr Exec - Digital Products Mgmt'],
      ['Majed Ahmed Mohamed Almarzouqi',         '30523',    'Analyst - Business Relationship Mgmt'],
      ['Khalid Abdulmunem Ibrahim Bin Kalban',   '29184',    'Mgr - Digital Products Mgmt'],
      ['Hamid Jasim Ahmed Mohamed Al-Ali',       '29295',    'Asst Mgr - Future Foresight Tech'],
      ['Elsa Prakash',                           '25297',    'Sr Analyst - Digital Product Mgmt'],
      ['Ummehani Rangwala',                      '99007665', 'External Contractor'],
      ['Rakesh Gulaganji',                       '99009102', 'External Contractor'],
      ['Prasanth Pillai',                        '99009407', 'External Contractor'],
      ['Marouane Basta',                         '99000698', 'External Contractor'],
      ['Ismail',                                 '',         'External Contractor'],
      ['Chandini',                               '',         'External Contractor'],
      ['Saravana Kumar',                         '',         'External Contractor'],
      ['Nixson Jesuraj',                         '',         'External Contractor'],
      ['Ankur Jain',                             '',         'External Contractor'],
      ['Anusha',                                 '',         'External Contractor'],
      ['Nora',                                   '',         'External Contractor'],
      ['Ashwin Kaleeswaran',                     '',         'External Contractor'],
      ['Fatehali',                               '',         'External Contractor'],
      ['Tharakeswar',                            '',         'External Contractor'],
      ['Kamalesh',                               '',         'External Contractor'],
      ['Kamlesh Manker',                         '99006876', 'External Contractor'],
    ];
    const seedPeople = db.transaction(() => {
      for (const [name, pr, des] of teamMembers) insertPerson.run(name, pr, des);
    });
    seedPeople();
    db.prepare("INSERT OR REPLACE INTO settings (key, value) VALUES ('people_v2', 'true')").run();
  }
}
```

Note: the old closing `}` of seedData() is replaced by the one at the end of the block above.

---

## 2. `/Users/khalidbinkalban/my-workspace/server/routes/people.js`

Replace the entire file with:

```js
const express = require('express');
const router = express.Router();
const db = require('../db');

// GET /api/people
router.get('/', (req, res) => {
  res.json(db.prepare('SELECT * FROM people ORDER BY name').all());
});

// GET /api/people/:name/tasks
router.get('/:name/tasks', (req, res) => {
  const tasks = db.prepare(`
    SELECT t.*, p.name as project_name
    FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.assignee = ?
    ORDER BY CASE t.status WHEN 'Open' THEN 1 WHEN 'In Progress' THEN 2 WHEN 'Waiting' THEN 3 ELSE 4 END, t.created_at DESC
  `).all(req.params.name);
  res.json(tasks);
});

// POST /api/people
router.post('/', (req, res) => {
  const { name, pr_number = '', grade = '', designation = '', department = '', section = '', reporting_line = '', mobile_number = '', place = '' } = req.body;
  if (!name) return res.status(400).json({ error: 'name is required' });
  try {
    const result = db.prepare(`
      INSERT INTO people (name, pr_number, grade, designation, department, section, reporting_line, mobile_number, place)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run(name, pr_number, grade, designation, department, section, reporting_line, mobile_number, place);
    res.json(db.prepare('SELECT * FROM people WHERE id = ?').get(result.lastInsertRowid));
  } catch (e) {
    res.status(409).json({ error: 'Person already exists' });
  }
});

// PUT /api/people/:id
router.put('/:id', (req, res) => {
  const { name, pr_number = '', grade = '', designation = '', department = '', section = '', reporting_line = '', mobile_number = '', place = '' } = req.body;
  if (!name) return res.status(400).json({ error: 'name is required' });
  const existing = db.prepare('SELECT * FROM people WHERE id = ?').get(req.params.id);
  if (!existing) return res.status(404).json({ error: 'Not found' });
  try {
    db.prepare(`
      UPDATE people SET name=?, pr_number=?, grade=?, designation=?, department=?, section=?, reporting_line=?, mobile_number=?, place=?
      WHERE id=?
    `).run(name, pr_number, grade, designation, department, section, reporting_line, mobile_number, place, req.params.id);
    res.json(db.prepare('SELECT * FROM people WHERE id = ?').get(req.params.id));
  } catch (e) {
    res.status(409).json({ error: 'Name already exists' });
  }
});

// DELETE /api/people/:id
router.delete('/:id', (req, res) => {
  db.prepare('DELETE FROM people WHERE id = ?').run(req.params.id);
  res.json({ ok: true });
});

module.exports = router;
```

---

## 3. `/Users/khalidbinkalban/my-workspace/client/src/components/People.jsx`

Replace the entire file with this complete redesign:

```jsx
import { useState, useEffect } from 'react';
import { Users, Plus, User, X, Pencil, Trash2, Search, ChevronUp, ChevronDown, Phone, MapPin, Building2, Layers, GitBranch, BadgeCheck } from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import ConfirmDialog from './ConfirmDialog';

const FIELDS = [
  { key: 'pr_number',     label: 'PR No.',        icon: BadgeCheck,  placeholder: 'e.g. 29184'         },
  { key: 'grade',         label: 'Grade',         icon: Layers,      placeholder: 'e.g. G8'            },
  { key: 'designation',   label: 'Designation',   icon: BadgeCheck,  placeholder: 'e.g. Mgr - Digital' },
  { key: 'department',    label: 'Department',    icon: Building2,   placeholder: 'e.g. Digital'       },
  { key: 'section',       label: 'Section',       icon: Layers,      placeholder: 'e.g. AI Strategy'  },
  { key: 'reporting_line',label: 'Reporting Line',icon: GitBranch,   placeholder: 'Reports to…'        },
  { key: 'mobile_number', label: 'Mobile',        icon: Phone,       placeholder: '+971 50 …'          },
  { key: 'place',         label: 'Place',         icon: MapPin,      placeholder: 'e.g. HQ'            },
];

const EMPTY_FORM = { name: '', pr_number: '', grade: '', designation: '', department: '', section: '', reporting_line: '', mobile_number: '', place: '' };

function Avatar({ name, size = 'md' }) {
  const s = size === 'lg' ? 'w-11 h-11 text-sm' : size === 'sm' ? 'w-7 h-7 text-[10px]' : 'w-9 h-9 text-xs';
  const initials = name && name !== 'Unassigned'
    ? name.split(' ').slice(0, 2).map(w => w[0]).join('').toUpperCase()
    : null;
  return (
    <div className={`${s} rounded-full bg-gradient-to-br from-primary to-brand-pink flex items-center justify-center text-white font-bold shrink-0`}>
      {initials || <User size={14} />}
    </div>
  );
}

function PersonModal({ person, onSave, onClose }) {
  const [form, setForm] = useState({ ...EMPTY_FORM, ...person });
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  return (
    <div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center p-0 sm:p-4" onClick={onClose}>
      <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-lg rounded-t-2xl sm:rounded-2xl shadow-2xl border border-gray-200 dark:border-gray-800 max-h-[92vh] overflow-y-auto" 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">
          <Avatar name={form.name || '?'} size="md" />
          <div className="flex-1">
            <h2 className="font-bold text-gray-900 dark:text-white">{person?.id ? 'Edit Member' : 'Add Member'}</h2>
            {person?.id && <p className="text-xs text-gray-400 mt-0.5">{person.name}</p>}
          </div>
          <button onClick={onClose} className="btn-icon"><X size={16} /></button>
        </div>
        <form onSubmit={e => { e.preventDefault(); onSave(form); }} className="px-5 py-4 space-y-3">
          <div>
            <label className="block text-xs font-medium text-gray-500 mb-1">Full Name *</label>
            <input className="input font-medium" required placeholder="Full name" value={form.name} onChange={e => set('name', e.target.value)} autoFocus />
          </div>
          <div className="grid grid-cols-2 gap-3">
            {FIELDS.map(({ key, label, placeholder }) => (
              <div key={key}>
                <label className="block text-xs font-medium text-gray-500 mb-1">{label}</label>
                <input className="input text-sm" placeholder={placeholder} value={form[key] || ''} onChange={e => set(key, e.target.value)} />
              </div>
            ))}
          </div>
          <div className="flex gap-3 justify-end pt-2">
            <button type="button" onClick={onClose} className="btn-secondary">Cancel</button>
            <button type="submit" className="btn-primary">{person?.id ? 'Save' : 'Add Member'}</button>
          </div>
        </form>
      </div>
    </div>
  );
}

export default function People() {
  const [people, setPeople]         = useState([]);
  const [loading, setLoading]       = useState(true);
  const [search, setSearch]         = useState('');
  const [editPerson, setEditPerson] = useState(null);
  const [deletePerson, setDelPerson]= useState(null);
  const [showNew, setShowNew]       = useState(false);
  const [sortKey, setSortKey]       = useState('name');
  const [sortDir, setSortDir]       = useState('asc');

  const load = () => {
    setLoading(true);
    api.get('/people').then(setPeople).finally(() => setLoading(false));
  };
  useEffect(() => { load(); }, []);

  const handleSave = async (form) => {
    try {
      if (form.id) { await api.put(`/people/${form.id}`, form); toast.success('Updated'); }
      else         { await api.post('/people', form);            toast.success('Member added'); }
      setEditPerson(null); setShowNew(false); load();
    } catch (e) { toast.error(e.message); }
  };

  const handleDelete = async () => {
    await api.delete(`/people/${deletePerson.id}`);
    toast.success('Removed'); setDelPerson(null); load();
  };

  const toggleSort = (key) => {
    if (sortKey === key) setSortDir(d => d === 'asc' ? 'desc' : 'asc');
    else { setSortKey(key); setSortDir('asc'); }
  };

  const filtered = people
    .filter(p => !search || p.name.toLowerCase().includes(search.toLowerCase()) ||
      (p.designation || '').toLowerCase().includes(search.toLowerCase()) ||
      (p.department || '').toLowerCase().includes(search.toLowerCase()) ||
      (p.pr_number || '').includes(search))
    .sort((a, b) => {
      const av = (a[sortKey] || '').toLowerCase();
      const bv = (b[sortKey] || '').toLowerCase();
      return sortDir === 'asc' ? av.localeCompare(bv) : bv.localeCompare(av);
    });

  const SortIcon = ({ col }) => {
    if (sortKey !== col) return <ChevronUp size={10} className="text-gray-300 dark:text-gray-600" />;
    return sortDir === 'asc'
      ? <ChevronUp size={10} className="text-primary" />
      : <ChevronDown size={10} className="text-primary" />;
  };

  const TH = ({ col, label }) => (
    <th
      className="px-3 py-2.5 text-left text-[10px] font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:text-gray-800 dark:hover:text-white whitespace-nowrap select-none"
      onClick={() => toggleSort(col)}
    >
      <div className="flex items-center gap-1">{label}<SortIcon col={col} /></div>
    </th>
  );

  return (
    <div className="flex flex-col h-full overflow-hidden">
      {/* Header */}
      <div className="shrink-0 px-4 pt-4 pb-3 bg-white dark:bg-gray-900 border-b border-gray-100 dark:border-gray-800">
        <div className="flex items-center justify-between mb-3">
          <div>
            <h1 className="text-lg font-bold text-gray-900 dark:text-white">My Team</h1>
            {!loading && <p className="text-xs text-gray-400 mt-0.5">{people.length} members</p>}
          </div>
          <button onClick={() => setShowNew(true)} className="btn-primary flex items-center gap-1.5 py-2 text-sm">
            <Plus size={13} /> Add Member
          </button>
        </div>
        <div className="relative">
          <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 by name, designation, department, PR…" value={search} onChange={e => setSearch(e.target.value)} />
        </div>
      </div>

      {/* Table */}
      <div className="flex-1 overflow-auto">
        {loading ? (
          <div className="p-4 space-y-2">
            {[...Array(8)].map((_, i) => <div key={i} className="skeleton h-12 rounded-xl" />)}
          </div>
        ) : filtered.length === 0 ? (
          <div className="flex flex-col items-center justify-center h-full gap-3 py-16">
            <Users size={44} className="text-gray-200 dark:text-gray-700" />
            <p className="text-gray-500 font-medium">{search ? 'No members found' : 'No team members yet'}</p>
          </div>
        ) : (
          <table className="w-full min-w-[900px] border-collapse">
            <thead className="sticky top-0 bg-gray-50 dark:bg-gray-800/80 backdrop-blur border-b border-gray-100 dark:border-gray-800 z-10">
              <tr>
                <th className="px-4 py-2.5 text-left text-[10px] font-semibold text-gray-500 uppercase tracking-wider w-8">#</th>
                <TH col="name"          label="Name" />
                <TH col="pr_number"     label="PR No." />
                <TH col="grade"         label="Grade" />
                <TH col="designation"   label="Designation" />
                <TH col="department"    label="Department" />
                <TH col="section"       label="Section" />
                <TH col="reporting_line"label="Reporting Line" />
                <TH col="mobile_number" label="Mobile" />
                <TH col="place"         label="Place" />
                <th className="px-3 py-2.5 w-16" />
              </tr>
            </thead>
            <tbody className="divide-y divide-gray-50 dark:divide-gray-800/60">
              {filtered.map((p, i) => (
                <tr key={p.id} className="group hover:bg-primary/5 dark:hover:bg-primary/10 transition-colors">
                  <td className="px-4 py-3 text-xs text-gray-400">{i + 1}</td>
                  <td className="px-3 py-3 min-w-[180px]">
                    <div className="flex items-center gap-2.5">
                      <Avatar name={p.name} size="sm" />
                      <span className="text-sm font-semibold text-gray-800 dark:text-gray-100 leading-snug">{p.name}</span>
                    </div>
                  </td>
                  <td className="px-3 py-3 whitespace-nowrap">
                    {p.pr_number
                      ? <span className="badge text-[10px] bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 font-mono">{p.pr_number}</span>
                      : <span className="text-gray-300 dark:text-gray-600 text-xs">—</span>}
                  </td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">{p.grade || <span className="text-gray-300 dark:text-gray-600">—</span>}</td>
                  <td className="px-3 py-3 text-xs text-gray-700 dark:text-gray-300 max-w-[180px]">
                    <span className="line-clamp-1">{p.designation || <span className="text-gray-300 dark:text-gray-600">—</span>}</span>
                  </td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">{p.department || <span className="text-gray-300 dark:text-gray-600">—</span>}</td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">{p.section || <span className="text-gray-300 dark:text-gray-600">—</span>}</td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">{p.reporting_line || <span className="text-gray-300 dark:text-gray-600">—</span>}</td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">
                    {p.mobile_number
                      ? <a href={`tel:${p.mobile_number}`} className="text-primary hover:underline" onClick={e => e.stopPropagation()}>{p.mobile_number}</a>
                      : <span className="text-gray-300 dark:text-gray-600">—</span>}
                  </td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">{p.place || <span className="text-gray-300 dark:text-gray-600">—</span>}</td>
                  <td className="px-3 py-3">
                    <div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
                      <button onClick={() => setEditPerson(p)} className="w-6 h-6 rounded-lg bg-gray-100 dark:bg-gray-800 flex items-center justify-center text-gray-400 hover:text-primary hover:bg-primary/10 transition-colors">
                        <Pencil size={11} />
                      </button>
                      <button onClick={() => setDelPerson(p)} className="w-6 h-6 rounded-lg bg-gray-100 dark:bg-gray-800 flex items-center justify-center text-gray-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors">
                        <Trash2 size={11} />
                      </button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>

      {(showNew || editPerson) && (
        <PersonModal person={editPerson} onSave={handleSave} onClose={() => { setShowNew(false); setEditPerson(null); }} />
      )}
      {deletePerson && (
        <ConfirmDialog title="Remove member?" message={`Remove "${deletePerson.name}" from your team?`} onConfirm={handleDelete} onCancel={() => setDelPerson(null)} />
      )}
    </div>
  );
}
```

Important notes:
- Read each file first before editing
- Make the changes carefully, especially the db.js seedData function replacement - the old people seed block ends with `seedPeople();` and then a closing `}` — replace from the `const insertPerson` line to the end of `seedData()` function's closing brace
- The `people_v2` flag uses `INSERT OR REPLACE` so it works even if the key already exists from a previous run


All three files have been updated. Here's a summary of what was done:

**`server/db.js`**
- Added the migration block after `db.pragma('foreign_keys = ON')` to safely `ALTER TABLE people` and add the 8 new columns (`pr_number`, `grade`, `designation`, `department`, `section`, `reporting_line`, `mobile_number`, `place`) — wrapped in try/catch so it's idempotent.
- Replaced the old simple people seed (20 generic names, no extra fields) with the new `people_v2`-gated block that clears the old records and inserts 31 real CAIO Batch 3 team members with PR numbers and designations. The `people_v2` flag uses `INSERT OR REPLACE` so it's safe on re-runs.

**`server/routes/people.js`**
- Replaced the minimal 3-route file with a full CRUD API: GET all, GET tasks by name, POST (create), PUT (update), DELETE — all handling the 8 new fields.

**`client/src/components/People.jsx`**
- Completely redesigned from the old avatar-grid + task-panel layout into a sortable, searchable data table showing all 10 columns (name, PR, grade, designation, department, section, reporting line, mobile, place, actions).
- Added `PersonModal` for add/edit with all fields, inline delete confirmation via `ConfirmDialog`, and hover-reveal edit/delete buttons per row.

agentId: a195b103dd6a61621 (use SendMessage with to: 'a195b103dd6a61621' to continue this agent)
<usage>total_tokens: 29663
tool_uses: 7
duration_ms: 100852</usage>

Prompt:
You need to make changes across 3 files in a Node.js + React + SQLite workspace app. Here are the exact changes needed:

---

## 1. `/Users/khalidbinkalban/my-workspace/server/db.js`

After the `db.pragma('foreign_keys = ON');` line (around line 11) and BEFORE the `function initDb()` declaration, add this migration block:

```js
// Migrate people table — add columns if not present
;['pr_number','grade','designation','department','section','reporting_line','mobile_number','place'].forEach(col => {
  try { db.exec(`ALTER TABLE people ADD COLUMN ${col} TEXT DEFAULT ''`); } catch {}
});
```

Then, inside `function seedData()`, find the people section (around line 171 starting with `const insertPerson = db.prepare('INSERT OR IGNORE INTO people (name) VALUES (?)')`). Replace EVERYTHING from that `const insertPerson` line through the end of `seedData()` (the closing `}`) with this:

```js
  // Seed people from CAIO Batch 3 - AlSheraa Shift (yellow-highlighted)
  const seededPeopleV2 = db.prepare("SELECT value FROM settings WHERE key = 'people_v2'").get();
  if (!seededPeopleV2) {
    db.prepare('DELETE FROM people').run();
    const insertPerson = db.prepare(`
      INSERT OR IGNORE INTO people (name, pr_number, designation)
      VALUES (?, ?, ?)
    `);
    const teamMembers = [
      ['Ayesha Mohd Ali Sarwashi',              '15610',    'Specialist - Digital Services'],
      ['Saleha Obaid Essa',                      '24290',    'Sr Demand Analyst'],
      ['Mohammad Saif Bin Sulaiman',             '29433',    'Sr Engr - Digital Asset Mgmt'],
      ['Eiman Abdulla Alkindi',                  '23463',    'Sr Engr - Projects Govrnce'],
      ['Ahmad Mohamad Bin Dakhan',               '25260',    'Sr Exec - Digital Comm'],
      ['Noora Hassan Abdullatif Alsarah',        '24743',    'Sr Officer - Digital Products Mgmt'],
      ['Ahmed Saleh Ahmed Alshehhi',             '22990',    'Sr Exec - Customer Haps'],
      ['Wafa Mohd Mubarak Albalooshi',           '24911',    'Sr Exec - Admin'],
      ['Ayesha Eissa Humaid Abdulla Alzaabi',    '26971',    'Sr Exec - Admin'],
      ['Shamsa Ahmad Abdulla Alabdulla',         '29509',    'Sr Officer - Portfolio Delivery Mgmt'],
      ['Abdulla Almuhairi',                      '14933',    'Sr Exec - Digital Products Mgmt'],
      ['Majed Ahmed Mohamed Almarzouqi',         '30523',    'Analyst - Business Relationship Mgmt'],
      ['Khalid Abdulmunem Ibrahim Bin Kalban',   '29184',    'Mgr - Digital Products Mgmt'],
      ['Hamid Jasim Ahmed Mohamed Al-Ali',       '29295',    'Asst Mgr - Future Foresight Tech'],
      ['Elsa Prakash',                           '25297',    'Sr Analyst - Digital Product Mgmt'],
      ['Ummehani Rangwala',                      '99007665', 'External Contractor'],
      ['Rakesh Gulaganji',                       '99009102', 'External Contractor'],
      ['Prasanth Pillai',                        '99009407', 'External Contractor'],
      ['Marouane Basta',                         '99000698', 'External Contractor'],
      ['Ismail',                                 '',         'External Contractor'],
      ['Chandini',                               '',         'External Contractor'],
      ['Saravana Kumar',                         '',         'External Contractor'],
      ['Nixson Jesuraj',                         '',         'External Contractor'],
      ['Ankur Jain',                             '',         'External Contractor'],
      ['Anusha',                                 '',         'External Contractor'],
      ['Nora',                                   '',         'External Contractor'],
      ['Ashwin Kaleeswaran',                     '',         'External Contractor'],
      ['Fatehali',                               '',         'External Contractor'],
      ['Tharakeswar',                            '',         'External Contractor'],
      ['Kamalesh',                               '',         'External Contractor'],
      ['Kamlesh Manker',                         '99006876', 'External Contractor'],
    ];
    const seedPeople = db.transaction(() => {
      for (const [name, pr, des] of teamMembers) insertPerson.run(name, pr, des);
    });
    seedPeople();
    db.prepare("INSERT OR REPLACE INTO settings (key, value) VALUES ('people_v2', 'true')").run();
  }
}
```

Note: the old closing `}` of seedData() is replaced by the one at the end of the block above.

---

## 2. `/Users/khalidbinkalban/my-workspace/server/routes/people.js`

Replace the entire file with:

```js
const express = require('express');
const router = express.Router();
const db = require('../db');

// GET /api/people
router.get('/', (req, res) => {
  res.json(db.prepare('SELECT * FROM people ORDER BY name').all());
});

// GET /api/people/:name/tasks
router.get('/:name/tasks', (req, res) => {
  const tasks = db.prepare(`
    SELECT t.*, p.name as project_name
    FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.assignee = ?
    ORDER BY CASE t.status WHEN 'Open' THEN 1 WHEN 'In Progress' THEN 2 WHEN 'Waiting' THEN 3 ELSE 4 END, t.created_at DESC
  `).all(req.params.name);
  res.json(tasks);
});

// POST /api/people
router.post('/', (req, res) => {
  const { name, pr_number = '', grade = '', designation = '', department = '', section = '', reporting_line = '', mobile_number = '', place = '' } = req.body;
  if (!name) return res.status(400).json({ error: 'name is required' });
  try {
    const result = db.prepare(`
      INSERT INTO people (name, pr_number, grade, designation, department, section, reporting_line, mobile_number, place)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run(name, pr_number, grade, designation, department, section, reporting_line, mobile_number, place);
    res.json(db.prepare('SELECT * FROM people WHERE id = ?').get(result.lastInsertRowid));
  } catch (e) {
    res.status(409).json({ error: 'Person already exists' });
  }
});

// PUT /api/people/:id
router.put('/:id', (req, res) => {
  const { name, pr_number = '', grade = '', designation = '', department = '', section = '', reporting_line = '', mobile_number = '', place = '' } = req.body;
  if (!name) return res.status(400).json({ error: 'name is required' });
  const existing = db.prepare('SELECT * FROM people WHERE id = ?').get(req.params.id);
  if (!existing) return res.status(404).json({ error: 'Not found' });
  try {
    db.prepare(`
      UPDATE people SET name=?, pr_number=?, grade=?, designation=?, department=?, section=?, reporting_line=?, mobile_number=?, place=?
      WHERE id=?
    `).run(name, pr_number, grade, designation, department, section, reporting_line, mobile_number, place, req.params.id);
    res.json(db.prepare('SELECT * FROM people WHERE id = ?').get(req.params.id));
  } catch (e) {
    res.status(409).json({ error: 'Name already exists' });
  }
});

// DELETE /api/people/:id
router.delete('/:id', (req, res) => {
  db.prepare('DELETE FROM people WHERE id = ?').run(req.params.id);
  res.json({ ok: true });
});

module.exports = router;
```

---

## 3. `/Users/khalidbinkalban/my-workspace/client/src/components/People.jsx`

Replace the entire file with this complete redesign:

```jsx
import { useState, useEffect } from 'react';
import { Users, Plus, User, X, Pencil, Trash2, Search, ChevronUp, ChevronDown, Phone, MapPin, Building2, Layers, GitBranch, BadgeCheck } from 'lucide-react';
import toast from 'react-hot-toast';
import api from '../api';
import ConfirmDialog from './ConfirmDialog';

const FIELDS = [
  { key: 'pr_number',     label: 'PR No.',        icon: BadgeCheck,  placeholder: 'e.g. 29184'         },
  { key: 'grade',         label: 'Grade',         icon: Layers,      placeholder: 'e.g. G8'            },
  { key: 'designation',   label: 'Designation',   icon: BadgeCheck,  placeholder: 'e.g. Mgr - Digital' },
  { key: 'department',    label: 'Department',    icon: Building2,   placeholder: 'e.g. Digital'       },
  { key: 'section',       label: 'Section',       icon: Layers,      placeholder: 'e.g. AI Strategy'  },
  { key: 'reporting_line',label: 'Reporting Line',icon: GitBranch,   placeholder: 'Reports to…'        },
  { key: 'mobile_number', label: 'Mobile',        icon: Phone,       placeholder: '+971 50 …'          },
  { key: 'place',         label: 'Place',         icon: MapPin,      placeholder: 'e.g. HQ'            },
];

const EMPTY_FORM = { name: '', pr_number: '', grade: '', designation: '', department: '', section: '', reporting_line: '', mobile_number: '', place: '' };

function Avatar({ name, size = 'md' }) {
  const s = size === 'lg' ? 'w-11 h-11 text-sm' : size === 'sm' ? 'w-7 h-7 text-[10px]' : 'w-9 h-9 text-xs';
  const initials = name && name !== 'Unassigned'
    ? name.split(' ').slice(0, 2).map(w => w[0]).join('').toUpperCase()
    : null;
  return (
    <div className={`${s} rounded-full bg-gradient-to-br from-primary to-brand-pink flex items-center justify-center text-white font-bold shrink-0`}>
      {initials || <User size={14} />}
    </div>
  );
}

function PersonModal({ person, onSave, onClose }) {
  const [form, setForm] = useState({ ...EMPTY_FORM, ...person });
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  return (
    <div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center p-0 sm:p-4" onClick={onClose}>
      <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-lg rounded-t-2xl sm:rounded-2xl shadow-2xl border border-gray-200 dark:border-gray-800 max-h-[92vh] overflow-y-auto" 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">
          <Avatar name={form.name || '?'} size="md" />
          <div className="flex-1">
            <h2 className="font-bold text-gray-900 dark:text-white">{person?.id ? 'Edit Member' : 'Add Member'}</h2>
            {person?.id && <p className="text-xs text-gray-400 mt-0.5">{person.name}</p>}
          </div>
          <button onClick={onClose} className="btn-icon"><X size={16} /></button>
        </div>
        <form onSubmit={e => { e.preventDefault(); onSave(form); }} className="px-5 py-4 space-y-3">
          <div>
            <label className="block text-xs font-medium text-gray-500 mb-1">Full Name *</label>
            <input className="input font-medium" required placeholder="Full name" value={form.name} onChange={e => set('name', e.target.value)} autoFocus />
          </div>
          <div className="grid grid-cols-2 gap-3">
            {FIELDS.map(({ key, label, placeholder }) => (
              <div key={key}>
                <label className="block text-xs font-medium text-gray-500 mb-1">{label}</label>
                <input className="input text-sm" placeholder={placeholder} value={form[key] || ''} onChange={e => set(key, e.target.value)} />
              </div>
            ))}
          </div>
          <div className="flex gap-3 justify-end pt-2">
            <button type="button" onClick={onClose} className="btn-secondary">Cancel</button>
            <button type="submit" className="btn-primary">{person?.id ? 'Save' : 'Add Member'}</button>
          </div>
        </form>
      </div>
    </div>
  );
}

export default function People() {
  const [people, setPeople]         = useState([]);
  const [loading, setLoading]       = useState(true);
  const [search, setSearch]         = useState('');
  const [editPerson, setEditPerson] = useState(null);
  const [deletePerson, setDelPerson]= useState(null);
  const [showNew, setShowNew]       = useState(false);
  const [sortKey, setSortKey]       = useState('name');
  const [sortDir, setSortDir]       = useState('asc');

  const load = () => {
    setLoading(true);
    api.get('/people').then(setPeople).finally(() => setLoading(false));
  };
  useEffect(() => { load(); }, []);

  const handleSave = async (form) => {
    try {
      if (form.id) { await api.put(`/people/${form.id}`, form); toast.success('Updated'); }
      else         { await api.post('/people', form);            toast.success('Member added'); }
      setEditPerson(null); setShowNew(false); load();
    } catch (e) { toast.error(e.message); }
  };

  const handleDelete = async () => {
    await api.delete(`/people/${deletePerson.id}`);
    toast.success('Removed'); setDelPerson(null); load();
  };

  const toggleSort = (key) => {
    if (sortKey === key) setSortDir(d => d === 'asc' ? 'desc' : 'asc');
    else { setSortKey(key); setSortDir('asc'); }
  };

  const filtered = people
    .filter(p => !search || p.name.toLowerCase().includes(search.toLowerCase()) ||
      (p.designation || '').toLowerCase().includes(search.toLowerCase()) ||
      (p.department || '').toLowerCase().includes(search.toLowerCase()) ||
      (p.pr_number || '').includes(search))
    .sort((a, b) => {
      const av = (a[sortKey] || '').toLowerCase();
      const bv = (b[sortKey] || '').toLowerCase();
      return sortDir === 'asc' ? av.localeCompare(bv) : bv.localeCompare(av);
    });

  const SortIcon = ({ col }) => {
    if (sortKey !== col) return <ChevronUp size={10} className="text-gray-300 dark:text-gray-600" />;
    return sortDir === 'asc'
      ? <ChevronUp size={10} className="text-primary" />
      : <ChevronDown size={10} className="text-primary" />;
  };

  const TH = ({ col, label }) => (
    <th
      className="px-3 py-2.5 text-left text-[10px] font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider cursor-pointer hover:text-gray-800 dark:hover:text-white whitespace-nowrap select-none"
      onClick={() => toggleSort(col)}
    >
      <div className="flex items-center gap-1">{label}<SortIcon col={col} /></div>
    </th>
  );

  return (
    <div className="flex flex-col h-full overflow-hidden">
      {/* Header */}
      <div className="shrink-0 px-4 pt-4 pb-3 bg-white dark:bg-gray-900 border-b border-gray-100 dark:border-gray-800">
        <div className="flex items-center justify-between mb-3">
          <div>
            <h1 className="text-lg font-bold text-gray-900 dark:text-white">My Team</h1>
            {!loading && <p className="text-xs text-gray-400 mt-0.5">{people.length} members</p>}
          </div>
          <button onClick={() => setShowNew(true)} className="btn-primary flex items-center gap-1.5 py-2 text-sm">
            <Plus size={13} /> Add Member
          </button>
        </div>
        <div className="relative">
          <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 by name, designation, department, PR…" value={search} onChange={e => setSearch(e.target.value)} />
        </div>
      </div>

      {/* Table */}
      <div className="flex-1 overflow-auto">
        {loading ? (
          <div className="p-4 space-y-2">
            {[...Array(8)].map((_, i) => <div key={i} className="skeleton h-12 rounded-xl" />)}
          </div>
        ) : filtered.length === 0 ? (
          <div className="flex flex-col items-center justify-center h-full gap-3 py-16">
            <Users size={44} className="text-gray-200 dark:text-gray-700" />
            <p className="text-gray-500 font-medium">{search ? 'No members found' : 'No team members yet'}</p>
          </div>
        ) : (
          <table className="w-full min-w-[900px] border-collapse">
            <thead className="sticky top-0 bg-gray-50 dark:bg-gray-800/80 backdrop-blur border-b border-gray-100 dark:border-gray-800 z-10">
              <tr>
                <th className="px-4 py-2.5 text-left text-[10px] font-semibold text-gray-500 uppercase tracking-wider w-8">#</th>
                <TH col="name"          label="Name" />
                <TH col="pr_number"     label="PR No." />
                <TH col="grade"         label="Grade" />
                <TH col="designation"   label="Designation" />
                <TH col="department"    label="Department" />
                <TH col="section"       label="Section" />
                <TH col="reporting_line"label="Reporting Line" />
                <TH col="mobile_number" label="Mobile" />
                <TH col="place"         label="Place" />
                <th className="px-3 py-2.5 w-16" />
              </tr>
            </thead>
            <tbody className="divide-y divide-gray-50 dark:divide-gray-800/60">
              {filtered.map((p, i) => (
                <tr key={p.id} className="group hover:bg-primary/5 dark:hover:bg-primary/10 transition-colors">
                  <td className="px-4 py-3 text-xs text-gray-400">{i + 1}</td>
                  <td className="px-3 py-3 min-w-[180px]">
                    <div className="flex items-center gap-2.5">
                      <Avatar name={p.name} size="sm" />
                      <span className="text-sm font-semibold text-gray-800 dark:text-gray-100 leading-snug">{p.name}</span>
                    </div>
                  </td>
                  <td className="px-3 py-3 whitespace-nowrap">
                    {p.pr_number
                      ? <span className="badge text-[10px] bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 font-mono">{p.pr_number}</span>
                      : <span className="text-gray-300 dark:text-gray-600 text-xs">—</span>}
                  </td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">{p.grade || <span className="text-gray-300 dark:text-gray-600">—</span>}</td>
                  <td className="px-3 py-3 text-xs text-gray-700 dark:text-gray-300 max-w-[180px]">
                    <span className="line-clamp-1">{p.designation || <span className="text-gray-300 dark:text-gray-600">—</span>}</span>
                  </td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">{p.department || <span className="text-gray-300 dark:text-gray-600">—</span>}</td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">{p.section || <span className="text-gray-300 dark:text-gray-600">—</span>}</td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">{p.reporting_line || <span className="text-gray-300 dark:text-gray-600">—</span>}</td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">
                    {p.mobile_number
                      ? <a href={`tel:${p.mobile_number}`} className="text-primary hover:underline" onClick={e => e.stopPropagation()}>{p.mobile_number}</a>
                      : <span className="text-gray-300 dark:text-gray-600">—</span>}
                  </td>
                  <td className="px-3 py-3 text-xs text-gray-600 dark:text-gray-400 whitespace-nowrap">{p.place || <span className="text-gray-300 dark:text-gray-600">—</span>}</td>
                  <td className="px-3 py-3">
                    <div className="flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
                      <button onClick={() => setEditPerson(p)} className="w-6 h-6 rounded-lg bg-gray-100 dark:bg-gray-800 flex items-center justify-center text-gray-400 hover:text-primary hover:bg-primary/10 transition-colors">
                        <Pencil size={11} />
                      </button>
                      <button onClick={() => setDelPerson(p)} className="w-6 h-6 rounded-lg bg-gray-100 dark:bg-gray-800 flex items-center justify-center text-gray-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors">
                        <Trash2 size={11} />
                      </button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>

      {(showNew || editPerson) && (
        <PersonModal person={editPerson} onSave={handleSave} onClose={() => { setShowNew(false); setEditPerson(null); }} />
      )}
      {deletePerson && (
        <ConfirmDialog title="Remove member?" message={`Remove "${deletePerson.name}" from your team?`} onConfirm={handleDelete} onCancel={() => setDelPerson(null)} />
      )}
    </div>
  );
}
```

Important notes:
- Read each file first before editing
- Make the changes carefully, especially the db.js seedData function replacement - the old people seed block ends with `seedPeople();` and then a closing `}` — replace from the `const insertPerson` line to the end of `seedData()` function's closing brace
- The `people_v2` flag uses `INSERT OR REPLACE` so it works even if the key already exists from a previous run


Let me verify the changes landed correctly:


Bash

Check migration block was added


const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');

const DB_PATH = path.join(__dirname, '../data/workspace.db');
const dataDir = path.dirname(DB_PATH);
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });

const db = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');

// Migrate people table — add columns if not present
;['pr_number','grade','designation','department','section','reporting_line','mobile_number','place'].forEach(col => {
  try { db.exec(`ALTER TABLE people ADD COLUMN ${col} TEXT DEFAULT ''`); } catch {}
});

function initDb() {
  db.exec(`
    CREATE TABLE IF NOT EXISTS projects (


150:    ['Chandini to provide process map of features announcement', 'ai', 'High', 'Chandini', '', 1],
153:    ['I&TF Quarterly report — Wafa, Ayesha, Umme, Nixon, Chandini', 'gov', 'Urgent', '', '2026-03-27', 0],
177:  const seededPeopleV2 = db.prepare("SELECT value FROM settings WHERE key = 'people_v2'").get();
197:      ['Khalid Abdulmunem Ibrahim Bin Kalban',   '29184',    'Mgr - Digital Products Mgmt'],
198:      ['Hamid Jasim Ahmed Mohamed Al-Ali',       '29295',    'Asst Mgr - Future Foresight Tech'],
205:      ['Chandini',                               '',         'External Contractor'],
221:    db.prepare("INSERT OR REPLACE INTO settings (key, value) VALUES ('people_v2', 'true')").run();


38:router.put('/:id', (req, res) => {
55:router.delete('/:id', (req, res) => {


8:  { key: 'pr_number',     label: 'PR No.',        icon: BadgeCheck,  placeholder: 'e.g. 29184'         },
18:const EMPTY_FORM = { name: '', pr_number: '', grade: '', designation: '', department: '', section: '', reporting_line: '', mobile_number: '', place: '' };
32:function PersonModal({ person, onSave, onClose }) {
78:  const [sortKey, setSortKey]       = useState('name');
101:    if (sortKey === key) setSortDir(d => d === 'asc' ? 'desc' : 'asc');
109:      (p.pr_number || '').includes(search))
111:      const av = (a[sortKey] || '').toLowerCase();
112:      const bv = (b[sortKey] || '').toLowerCase();
117:    if (sortKey !== col) return <ChevronUp size={10} className="text-gray-300 dark:text-gray-600" />;
167:
168:
169:
170:
171:
172:

[Showing results with pagination = limit: 15]


Everything is in order. Now the database needs to apply the migration — since `people_v2` doesn't exist yet in the running DB, it will trigger on next server restart. The server just needs to be restarted to pick up the changes:


node -e "
const db = require('./server/db');
const people = db.prepare('SELECT * FROM people LIMIT 5').all();
console.log('People count:', db.prepare('SELECT COUNT(*) as c FROM people').get().c);
console.log('Sample:', people.map(p => p.name + ' | ' + p.pr_number + ' | ' + p.designation).join('\n'));
" 2>&1

Test DB migration and seeding


People count: 20
Sample: Chandini |  |
Hamid |  |
Wafa |  |
Ayesha |  |
Shabeer |  |


The `people_v2` seed is nested inside `seedData()` which only runs once (guarded by the `seeded` key). Since the DB is already seeded, it won't trigger. I need to move the `people_v2` block outside into `initDb()`: