I need you to build a complete Senior Manager personal workspace web application. This is a full-stack Node.js + React app. Here is everything you need to know:
WHO THIS IS FOR
A Senior Manager at DEWA (Dubai Electricity and Water Authority) who manages 20+ projects across AI strategy, vendor partnerships, governance, people management, events, and strategic initiatives. They work in English, use Android and desktop, and need a fast, beautiful, focused app.
TECH STACK

Backend: Node.js + Express
Frontend: React (Vite)
Database: SQLite with better-sqlite3 (persistent, local, no cloud needed)
AI: Anthropic Claude API (server-side only, never exposed to browser)
Voice: Web Speech API with graceful fallback
Styling: Tailwind CSS
No authentication needed (personal use)

FILE STRUCTURE TO CREATE
my-workspace/
├── server/
│   ├── index.js          # Express server
│   ├── db.js             # SQLite setup and queries
│   └── routes/
│       ├── tasks.js
│       ├── projects.js
│       ├── notes.js
│       └── ai.js         # AI inbox parser (Claude API)
├── client/
│   ├── index.html
│   ├── vite.config.js
│   └── src/
│       ├── main.jsx
│       ├── App.jsx
│       ├── index.css
│       └── components/
│           ├── Today.jsx
│           ├── Inbox.jsx
│           ├── Projects.jsx
│           ├── ProjectDetail.jsx
│           ├── Tasks.jsx
│           ├── Notes.jsx
│           ├── FollowUps.jsx
│           ├── People.jsx
│           ├── WeeklyReview.jsx
│           ├── Search.jsx
│           ├── TaskCard.jsx
│           ├── BottomNav.jsx
│           └── ConfirmDialog.jsx
├── .env
├── .env.example
├── package.json
└── README.md
DATABASE SCHEMA
Create these SQLite tables:
sqlprojects: id, name, category, status, priority, description, last_update, next_step, created_at, updated_at

tasks: id, title, notes, project_id, category, priority, status, assignee, due_date, is_follow_up, is_today_focus, completed_at, created_at, updated_at

notes: id, title, body, project_id, date, created_at

people: id, name, role, created_at

(tasks already has assignee as text — people is a separate lookup table for the people view)
CATEGORIES (use these exact ids and labels)
ai       → AI Adoption & Strategy      → blue
vendor   → Vendor & Partnerships       → purple
gov      → Governance & Reporting      → amber
people   → People & Team              → teal
events   → Events & External          → coral/orange
strategic → Strategic Initiatives     → green
adhoc    → CAIO / EVP Ad Hoc          → red
SEED DATA — PROJECTS (pre-load these)
AI Adoption & Strategy:
- Microsoft Copilot Rollout (In Progress, High)
- DEWA AI Academy (In Progress, High)
- AI Command Center RFP (Open, High)
- AI Champions Program (In Progress, High)
- AI OS Initiative (In Progress, Urgent)
- COE End-to-End (In Progress, High)
- GitHub Copilot Plan (Open, High)
- WalkMe & SAP Joule (In Progress, Medium)
- Rummana at Work (Open, Medium)
- Blink App AI Adoption (Open, Medium)

Vendor & Partnerships:
- Google Partnership & NDA (Waiting, Urgent)
- Polynome Engagement (In Progress, High)
- Genspark Engagement (Waiting, Medium)
- IMD Proposal (Open, Medium)
- Ducont Wave 2 (In Progress, High)
- ElevenLabs Engagement (Open, Medium)
- Dell Engagement (Open, Medium)
- Coursera / Udemy Licenses (In Progress, Medium)

Governance & Reporting:
- I&TF Quarterly Report (In Progress, Urgent)
- AI Charter & ISO 42001 (Open, High)
- KPIs & Blueprint (In Progress, High)
- IETF Benchmark (In Progress, High)

People & Team:
- Employees Rating (Open, High)
- Data Champions Program (Open, Medium)
- Outsource Assessment (In Progress, Medium)
- AI Ambassadors Survey (In Progress, High)

Events & External:
- WEF Lighthouse (Open, High)
- Gartner Data Summit (Open, Medium)
- GenAI Week Emirates Towers (Open, Medium)
- Microsoft Community Conference (Open, Medium)

Strategic Initiatives:
- AlHasbah Use Cases Phase I (In Progress, High)
- Digital DEWA Direction (Open, High)
- D2D Workshop (In Progress, High)
- AICC ROI Tracking (Open, High)

CAIO / EVP Ad Hoc:
- EVP MD Finance Agent (Open, Urgent)
- CAIO AI OS Initiative (In Progress, High)
SEED DATA — TASKS (pre-load these from the notebook)
- Google NDA: meet Legal Affairs + Hadi + CAIO (vendor, Urgent, assignee: Hadi, follow-up: true)
- Chandini to provide process map of features announcement (ai, High, assignee: Chandini, follow-up: true)
- Copilot ROI analysis (ai, High, assignee: Self)
- AlHasbah KPIs review with Shabeer (gov, High, assignee: Shabeer, follow-up: true)
- I&TF Quarterly report — Wafa, Ayesha, Umme, Nixon, Chandini (gov, Urgent, due: 2026-03-27)
- EVP asked for MD Finance Agent (adhoc, Urgent)
- WEF Lighthouse document — shared with Hamid and Ahmad AlShehhi (events, High, assignee: Hamid, follow-up: true)
- Check Udemy license distribution across divisions (ai, Medium)
- GitHub Copilot end-to-end plan (ai, High)
- Benchmark presentation meeting on 13 Apr (gov, High, assignee: Hamid, due: 2026-04-13, follow-up: true)
- Polynome NDA to be signed (vendor, High, follow-up: true)
- AI Ethics & Governance workshop for AI Champions (ai, High)
- Copilot Studio Kit demo with Yasa (ai, High, assignee: Yasa)
- Check with Microsoft to connect Viva Learning team (vendor, Medium)
- D2D issue still open (strategic, High)
- Staff mobile allowance (people, Medium)
- Universities update (events, Medium)
- Follow up on Anthropic contacts (vendor, High, follow-up: true)
- Genspark credit issue resolution (vendor, Medium, follow-up: true)
- Survey for AI Champions — measure performance (ai, High)
SEED DATA — PEOPLE
Chandini, Hamid, Wafa, Ayesha, Shabeer, Hadi, Abdulla, Ahmed, Yasa, Sunil, Umme, Nixon, Rakish, Ummehani, Saif, Santhosh, Abida, Marwan, Moaza, Meera AlKitbi
FEATURES TO BUILD

TODAY VIEW

Shows only tasks marked is_today_focus = true and status != Done
Date header with day + date
4 metric cards: Today's focus count, Total open tasks, Open follow-ups, Active projects
Task cards with: title, priority badge, category badge, status badge, assignee, due date (red if overdue)
Blue filled circle = pinned to today. Empty circle = not pinned. Click to toggle
Below pinned tasks: "Add to today" section showing top 5 urgent/high tasks not yet pinned
Empty state: friendly message + link to tasks

INBOX (AI-POWERED)

Large textarea for natural language input
Voice button using Web Speech API (shows "Listening..." state)
"Process" button → calls POST /api/ai/parse → returns structured JSON
Show parsed result as an EDITABLE card (user can change any field before saving)
Fields: title (editable text), type (task/note/project), category (dropdown), priority (dropdown), assignee (text), due date (date), follow-up (checkbox), notes (textarea)
"Save to workspace" confirms. "Edit & save" lets them modify first
"Discard" clears it
Show last 5 items added via inbox as history

PROJECTS VIEW

Category filter pills (scrollable, horizontal)
Status filter: All / Open / In Progress / Waiting / Done
Project cards showing: name, category badge, priority badge, status badge, open task count, last update date
Click project → opens ProjectDetail

New Project button

PROJECT DETAIL

Full page view for one project
Header: project name, category, priority, status (all editable inline)
"Last update" text field (what happened last)
"Next step" text field (what needs to happen)
Description/context textarea
Tasks tab: all tasks for this project, + Add Task button
Notes tab: all notes for this project, + Add Note button
Back button

TASKS VIEW

Search bar at top (searches title + notes + assignee)
Filter by status, category, priority
Sort by: due date, priority, created date
Task cards with all fields

New Task button

Bulk actions: mark selected as done

NOTES VIEW

Date-sorted, newest first
Each note shows: title, date, linked project (if any), preview of body
Click to expand full note

New Note button

FOLLOW-UPS VIEW

All tasks where is_follow_up = true and status != Done
Grouped by assignee
Shows how many days since created
"Nudge" button copies a follow-up message to clipboard: "Hi [name], just following up on: [task title]"

PEOPLE VIEW

List of all people
Click a person → see all tasks assigned to them grouped by status
Shows: waiting on them (follow-ups), tasks they own, completed by them

WEEKLY REVIEW VIEW

Tasks completed this week
Tasks added this week
Overdue tasks
Follow-ups older than 7 days
Projects with no activity this week
Button: "Start next week" → clears today focus, prompts to pick new focus items

SEARCH

Global search bar in header
Searches: task titles, notes titles+body, project names
Results grouped by type
Keyboard shortcut: Cmd+K / Ctrl+K

UI / UX REQUIREMENTS
Design philosophy: Clean, calm, professional. Think Linear + Notion + Agenda combined. Not corporate, not playful — focused.

Font: Inter (import from Google Fonts)
Color system: Use Tailwind. Primary accent: #185FA5 (blue). Category colors as defined above
Background: #FAFAFA (near white), cards: pure white with subtle border
Dark mode: supported via Tailwind dark: classes, toggle in header
Mobile-first: bottom navigation bar on mobile (Today, Inbox, Projects, Tasks, More)
Desktop: left sidebar navigation with icons + labels
Card design: clean white cards, 1px border (#E5E7EB), 8px radius, 16px padding, subtle hover state
Priority badges: Urgent=red, High=amber, Medium=blue, Low=green — all as small pills
Category badges: each category has its own color pill
Status badges: Open=gray, In Progress=blue, Waiting=amber, Done=green
Transitions: 150ms ease on hover, focus states on all inputs
Empty states: illustrated with a short message and a CTA button
Delete: always requires a confirmation dialog (ConfirmDialog component)
Responsive: works perfectly on 375px (iPhone SE) up to 1440px desktop
Loading states: skeleton loaders on all data fetches
Toast notifications: success/error on all save/delete actions

API ROUTES
GET    /api/projects
POST   /api/projects
PUT    /api/projects/:id
DELETE /api/projects/:id

GET    /api/tasks
POST   /api/tasks
PUT    /api/tasks/:id
DELETE /api/tasks/:id
PATCH  /api/tasks/:id/focus    (toggle today focus)
PATCH  /api/tasks/:id/status   (quick status update)

GET    /api/notes
POST   /api/notes
PUT    /api/notes/:id
DELETE /api/notes/:id

GET    /api/people
POST   /api/people

POST   /api/ai/parse           (receives text, returns parsed JSON via Claude API)

GET    /api/search?q=           (searches tasks + projects + notes)

GET    /api/weekly-review       (returns this week's stats)
AI PARSER PROMPT (use this in server/routes/ai.js)
System: You are a workspace assistant for a Senior Manager at DEWA (Dubai Electricity and Water Authority).
Extract structured data from natural language input and return ONLY valid JSON.

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

Return this exact structure:
{
  "type": "task" | "note" | "project",
  "title": "concise action-oriented title",
  "details": "full context preserved",
  "category": "category id",
  "priority": "Urgent" | "High" | "Medium" | "Low",
  "assignee": "person name or empty string",
  "due": "YYYY-MM-DD or empty string",
  "isFollowUp": true | false,
  "projectHint": "most likely related project name or empty string",
  "suggestedTodayFocus": true | false
}
ENV FILE
ANTHROPIC_API_KEY=your_key_here
PORT=3001
NODE_ENV=development
PACKAGE.JSON scripts
json"dev": "concurrently \"npm run server\" \"npm run client\"",
"server": "nodemon server/index.js",
"client": "vite client",
"build": "vite build client",
"start": "node server/index.js"
README — include:

Prerequisites
Installation steps
How to get Anthropic API key
How to run
How to use the Inbox
How to deploy (optional)

Build everything completely. All files. All components. Full working code. Seed the database on first run. Make it beautiful and functional.


## plugin:context7:context7
Use this server to fetch current documentation whenever the user asks about a library, framework, SDK, API, CLI tool, or cloud service -- even well-known ones like React, Next.js, Prisma, Express, Tailwind, Django, or Spring Boot. This includes API syntax, configuration, version migration, library-specific debugging, setup instructions, and CLI tool usage. Use even when you think you know the answer -- your training data may not reflect recent changes. Prefer this over web search for library docs.

Do not use for: refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts.


Bash

Check existing files in workspace


(Bash completed with no output)


mkdir -p /Users/khalidbinkalban/my-workspace/server/routes /Users/khalidbinkalban/my-workspace/client/src/components /Users/khalidbinkalban/my-workspace/data

Create directory structure


Now I'll build everything. Let me start with the foundational files in parallel:


Write

{
  "name": "my-workspace",
  "version": "1.0.0",
  "description": "Senior Manager Personal Workspace — DEWA",
  "scripts": {
    "dev": "concurrently \"npm run server\" \"npm run client\"",
    "server": "nodemon server/index.js",
    "client": "vite client",
    "build": "vite build client",
    "start": "node server/index.js"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.37.0",
    "better-sqlite3": "^11.7.0",
    "cors": "^2.8.5",
    "dotenv": "^16.4.7",
    "express": "^4.21.2",
    "lucide-react": "^0.469.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-hot-toast": "^2.4.1",
    "react-router-dom": "^6.28.0"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.3.1",
    "autoprefixer": "^10.4.20",
    "concurrently": "^9.1.2",
    "nodemon": "^3.1.9",
    "postcss": "^8.5.1",
    "tailwindcss": "^3.4.17",
    "vite": "^6.0.6"
  }
}


File created successfully at: /Users/khalidbinkalban/my-workspace/package.json

my-workspace

Senior Manager Personal Workspace — DEWA

concurrently "npm run server" "npm run client"


Write

ANTHROPIC_API_KEY=your_anthropic_api_key_here
PORT=3001
NODE_ENV=development


File created successfully at: /Users/khalidbinkalban/my-workspace/.env.example

ANTHROPIC_API_KEY=your_anthropic_api_key_here
PORT=3001
NODE_ENV=development


node_modules/
dist/
data/*.db
.env
.DS_Store


File created successfully at: /Users/khalidbinkalban/my-workspace/.gitignore

node_modules/
dist/
data/*.db
.env
.DS_Store


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;


File created successfully at: /Users/khalidbinkalban/my-workspace/server/db.js

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;


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

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

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

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

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

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
  console.log(`🚀 Server running on http://localhost:${PORT}`);
});


File created successfully at: /Users/khalidbinkalban/my-workspace/server/index.js

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

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

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

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

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

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
  console.log(`🚀 Server running on http://localhost:${PORT}`);
});


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

// GET /api/projects
router.get('/', (req, res) => {
  const { category, status } = req.query;
  let sql = `
    SELECT p.*,
      (SELECT COUNT(*) FROM tasks t WHERE t.project_id = p.id AND t.status != 'Done') as open_task_count
    FROM projects p
    WHERE 1=1
  `;
  const params = [];
  if (category) { sql += ' AND p.category = ?'; params.push(category); }
  if (status) { sql += ' AND p.status = ?'; params.push(status); }
  sql += ' ORDER BY CASE p.priority WHEN "Urgent" THEN 1 WHEN "High" THEN 2 WHEN "Medium" THEN 3 ELSE 4 END, p.name';
  res.json(db.prepare(sql).all(...params));
});

// GET /api/projects/:id
router.get('/:id', (req, res) => {
  const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(req.params.id);
  if (!project) return res.status(404).json({ error: 'Not found' });
  res.json(project);
});

// POST /api/projects
router.post('/', (req, res) => {
  const { name, category = 'adhoc', status = 'Open', priority = 'Medium', description = '', last_update = '', next_step = '' } = req.body;
  if (!name) return res.status(400).json({ error: 'name is required' });
  const result = db.prepare(`
    INSERT INTO projects (name, category, status, priority, description, last_update, next_step)
    VALUES (?, ?, ?, ?, ?, ?, ?)
  `).run(name, category, status, priority, description, last_update, next_step);
  res.json(db.prepare('SELECT * FROM projects WHERE id = ?').get(result.lastInsertRowid));
});

// PUT /api/projects/:id
router.put('/:id', (req, res) => {
  const { name, category, status, priority, description, last_update, next_step } = req.body;
  db.prepare(`
    UPDATE projects SET name=?, category=?, status=?, priority=?, description=?, last_update=?, next_step=?, updated_at=datetime('now')
    WHERE id=?
  `).run(name, category, status, priority, description, last_update, next_step, req.params.id);
  res.json(db.prepare('SELECT * FROM projects WHERE id = ?').get(req.params.id));
});

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

module.exports = router;


File created successfully at: /Users/khalidbinkalban/my-workspace/server/routes/projects.js

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

// GET /api/projects
router.get('/', (req, res) => {
  const { category, status } = req.query;
  let sql = `
    SELECT p.*,
      (SELECT COUNT(*) FROM tasks t WHERE t.project_id = p.id AND t.status != 'Done') as open_task_count
    FROM projects p
    WHERE 1=1
  `;
  const params = [];
  if (category) { sql += ' AND p.category = ?'; params.push(category); }
  if (status) { sql += ' AND p.status = ?'; params.push(status); }
  sql += ' ORDER BY CASE p.priority WHEN "Urgent" THEN 1 WHEN "High" THEN 2 WHEN "Medium" THEN 3 ELSE 4 END, p.name';
  res.json(db.prepare(sql).all(...params));
});

// GET /api/projects/:id
router.get('/:id', (req, res) => {
  const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(req.params.id);
  if (!project) return res.status(404).json({ error: 'Not found' });
  res.json(project);
});

// POST /api/projects
router.post('/', (req, res) => {
  const { name, category = 'adhoc', status = 'Open', priority = 'Medium', description = '', last_update = '', next_step = '' } = req.body;
  if (!name) return res.status(400).json({ error: 'name is required' });
  const result = db.prepare(`
    INSERT INTO projects (name, category, status, priority, description, last_update, next_step)
    VALUES (?, ?, ?, ?, ?, ?, ?)
  `).run(name, category, status, priority, description, last_update, next_step);
  res.json(db.prepare('SELECT * FROM projects WHERE id = ?').get(result.lastInsertRowid));
});

// PUT /api/projects/:id
router.put('/:id', (req, res) => {
  const { name, category, status, priority, description, last_update, next_step } = req.body;
  db.prepare(`
    UPDATE projects SET name=?, category=?, status=?, priority=?, description=?, last_update=?, next_step=?, updated_at=datetime('now')
    WHERE id=?
  `).run(name, category, status, priority, description, last_update, next_step, req.params.id);
  res.json(db.prepare('SELECT * FROM projects WHERE id = ?').get(req.params.id));
});

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

module.exports = router;


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

// GET /api/tasks
router.get('/', (req, res) => {
  const { project_id, status, category, priority, search, today_only, follow_up_only } = req.query;
  let sql = `
    SELECT t.*, p.name as project_name
    FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE 1=1
  `;
  const params = [];
  if (project_id) { sql += ' AND t.project_id = ?'; params.push(project_id); }
  if (status) { sql += ' AND t.status = ?'; params.push(status); }
  if (category) { sql += ' AND t.category = ?'; params.push(category); }
  if (priority) { sql += ' AND t.priority = ?'; params.push(priority); }
  if (today_only === 'true') { sql += " AND t.is_today_focus = 1 AND t.status != 'Done'"; }
  if (follow_up_only === 'true') { sql += " AND t.is_follow_up = 1 AND t.status != 'Done'"; }
  if (search) {
    sql += ' AND (t.title LIKE ? OR t.notes LIKE ? OR t.assignee LIKE ?)';
    const q = `%${search}%`;
    params.push(q, q, q);
  }
  sql += ` ORDER BY
    CASE t.priority WHEN 'Urgent' THEN 1 WHEN 'High' THEN 2 WHEN 'Medium' THEN 3 ELSE 4 END,
    CASE t.status WHEN 'Open' THEN 1 WHEN 'In Progress' THEN 2 WHEN 'Waiting' THEN 3 ELSE 4 END,
    t.due_date ASC, t.created_at DESC`;
  res.json(db.prepare(sql).all(...params));
});

// GET /api/tasks/stats
router.get('/stats', (req, res) => {
  const todayFocus = db.prepare("SELECT COUNT(*) as count FROM tasks WHERE is_today_focus = 1 AND status != 'Done'").get().count;
  const openTasks = db.prepare("SELECT COUNT(*) as count FROM tasks WHERE status != 'Done'").get().count;
  const openFollowUps = db.prepare("SELECT COUNT(*) as count FROM tasks WHERE is_follow_up = 1 AND status != 'Done'").get().count;
  const activeProjects = db.prepare("SELECT COUNT(*) as count FROM projects WHERE status != 'Done'").get().count;
  res.json({ todayFocus, openTasks, openFollowUps, activeProjects });
});

// GET /api/tasks/:id
router.get('/:id', (req, res) => {
  const task = db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id);
  if (!task) return res.status(404).json({ error: 'Not found' });
  res.json(task);
});

// POST /api/tasks
router.post('/', (req, res) => {
  const {
    title, notes = '', project_id = null, category = 'adhoc', priority = 'Medium',
    status = 'Open', assignee = '', due_date = '', is_follow_up = 0, is_today_focus = 0
  } = req.body;
  if (!title) return res.status(400).json({ error: 'title is required' });
  const result = db.prepare(`
    INSERT INTO tasks (title, notes, project_id, category, priority, status, assignee, due_date, is_follow_up, is_today_focus)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  `).run(title, notes, project_id, category, priority, status, assignee, due_date, is_follow_up ? 1 : 0, is_today_focus ? 1 : 0);
  res.json(db.prepare('SELECT * FROM tasks WHERE id = ?').get(result.lastInsertRowid));
});

// PUT /api/tasks/:id
router.put('/:id', (req, res) => {
  const {
    title, notes, project_id, category, priority, status, assignee, due_date, is_follow_up, is_today_focus
  } = req.body;
  const prev = db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id);
  if (!prev) return res.status(404).json({ error: 'Not found' });
  const completedAt = status === 'Done' && prev.status !== 'Done' ? new Date().toISOString() : (status !== 'Done' ? '' : prev.completed_at);
  db.prepare(`
    UPDATE tasks SET title=?, notes=?, project_id=?, category=?, priority=?, status=?, assignee=?, due_date=?,
    is_follow_up=?, is_today_focus=?, completed_at=?, updated_at=datetime('now')
    WHERE id=?
  `).run(title, notes, project_id, category, priority, status, assignee, due_date,
    is_follow_up ? 1 : 0, is_today_focus ? 1 : 0, completedAt, req.params.id);
  res.json(db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id));
});

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

// PATCH /api/tasks/:id/focus — toggle today focus
router.patch('/:id/focus', (req, res) => {
  const task = db.prepare('SELECT is_today_focus FROM tasks WHERE id = ?').get(req.params.id);
  if (!task) return res.status(404).json({ error: 'Not found' });
  const newVal = task.is_today_focus ? 0 : 1;
  db.prepare("UPDATE tasks SET is_today_focus=?, updated_at=datetime('now') WHERE id=?").run(newVal, req.params.id);
  res.json({ is_today_focus: newVal });
});

// PATCH /api/tasks/:id/status — quick status update
router.patch('/:id/status', (req, res) => {
  const { status } = req.body;
  const prev = db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id);
  if (!prev) return res.status(404).json({ error: 'Not found' });
  const completedAt = status === 'Done' ? new Date().toISOString() : '';
  db.prepare("UPDATE tasks SET status=?, completed_at=?, updated_at=datetime('now') WHERE id=?")
    .run(status, completedAt, req.params.id);
  res.json(db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id));
});

module.exports = router;


File created successfully at: /Users/khalidbinkalban/my-workspace/server/routes/tasks.js

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

// GET /api/tasks
router.get('/', (req, res) => {
  const { project_id, status, category, priority, search, today_only, follow_up_only } = req.query;
  let sql = `
    SELECT t.*, p.name as project_name
    FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE 1=1
  `;
  const params = [];
  if (project_id) { sql += ' AND t.project_id = ?'; params.push(project_id); }
  if (status) { sql += ' AND t.status = ?'; params.push(status); }
  if (category) { sql += ' AND t.category = ?'; params.push(category); }
  if (priority) { sql += ' AND t.priority = ?'; params.push(priority); }
  if (today_only === 'true') { sql += " AND t.is_today_focus = 1 AND t.status != 'Done'"; }
  if (follow_up_only === 'true') { sql += " AND t.is_follow_up = 1 AND t.status != 'Done'"; }
  if (search) {
    sql += ' AND (t.title LIKE ? OR t.notes LIKE ? OR t.assignee LIKE ?)';
    const q = `%${search}%`;
    params.push(q, q, q);
  }
  sql += ` ORDER BY
    CASE t.priority WHEN 'Urgent' THEN 1 WHEN 'High' THEN 2 WHEN 'Medium' THEN 3 ELSE 4 END,
    CASE t.status WHEN 'Open' THEN 1 WHEN 'In Progress' THEN 2 WHEN 'Waiting' THEN 3 ELSE 4 END,
    t.due_date ASC, t.created_at DESC`;
  res.json(db.prepare(sql).all(...params));
});

// GET /api/tasks/stats
router.get('/stats', (req, res) => {
  const todayFocus = db.prepare("SELECT COUNT(*) as count FROM tasks WHERE is_today_focus = 1 AND status != 'Done'").get().count;
  const openTasks = db.prepare("SELECT COUNT(*) as count FROM tasks WHERE status != 'Done'").get().count;
  const openFollowUps = db.prepare("SELECT COUNT(*) as count FROM tasks WHERE is_follow_up = 1 AND status != 'Done'").get().count;
  const activeProjects = db.prepare("SELECT COUNT(*) as count FROM projects WHERE status != 'Done'").get().count;
  res.json({ todayFocus, openTasks, openFollowUps, activeProjects });
});

// GET /api/tasks/:id
router.get('/:id', (req, res) => {
  const task = db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id);
  if (!task) return res.status(404).json({ error: 'Not found' });
  res.json(task);
});

// POST /api/tasks
router.post('/', (req, res) => {
  const {
    title, notes = '', project_id = null, category = 'adhoc', priority = 'Medium',
    status = 'Open', assignee = '', due_date = '', is_follow_up = 0, is_today_focus = 0
  } = req.body;
  if (!title) return res.status(400).json({ error: 'title is required' });
  const result = db.prepare(`
    INSERT INTO tasks (title, notes, project_id, category, priority, status, assignee, due_date, is_follow_up, is_today_focus)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  `).run(title, notes, project_id, category, priority, status, assignee, due_date, is_follow_up ? 1 : 0, is_today_focus ? 1 : 0);
  res.json(db.prepare('SELECT * FROM tasks WHERE id = ?').get(result.lastInsertRowid));
});

// PUT /api/tasks/:id
router.put('/:id', (req, res) => {
  const {
    title, notes, project_id, category, priority, status, assignee, due_date, is_follow_up, is_today_focus
  } = req.body;
  const prev = db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id);
  if (!prev) return res.status(404).json({ error: 'Not found' });
  const completedAt = status === 'Done' && prev.status !== 'Done' ? new Date().toISOString() : (status !== 'Done' ? '' : prev.completed_at);
  db.prepare(`
    UPDATE tasks SET title=?, notes=?, project_id=?, category=?, priority=?, status=?, assignee=?, due_date=?,
    is_follow_up=?, is_today_focus=?, completed_at=?, updated_at=datetime('now')
    WHERE id=?
  `).run(title, notes, project_id, category, priority, status, assignee, due_date,
    is_follow_up ? 1 : 0, is_today_focus ? 1 : 0, completedAt, req.params.id);
  res.json(db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id));
});

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

// PATCH /api/tasks/:id/focus — toggle today focus
router.patch('/:id/focus', (req, res) => {
  const task = db.prepare('SELECT is_today_focus FROM tasks WHERE id = ?').get(req.params.id);
  if (!task) return res.status(404).json({ error: 'Not found' });
  const newVal = task.is_today_focus ? 0 : 1;
  db.prepare("UPDATE tasks SET is_today_focus=?, updated_at=datetime('now') WHERE id=?").run(newVal, req.params.id);
  res.json({ is_today_focus: newVal });
});

// PATCH /api/tasks/:id/status — quick status update
router.patch('/:id/status', (req, res) => {
  const { status } = req.body;
  const prev = db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id);
  if (!prev) return res.status(404).json({ error: 'Not found' });
  const completedAt = status === 'Done' ? new Date().toISOString() : '';
  db.prepare("UPDATE tasks SET status=?, completed_at=?, updated_at=datetime('now') WHERE id=?")
    .run(status, completedAt, req.params.id);
  res.json(db.prepare('SELECT * FROM tasks WHERE id = ?').get(req.params.id));
});

module.exports = router;


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

// GET /api/notes
router.get('/', (req, res) => {
  const { project_id } = req.query;
  let sql = `
    SELECT n.*, p.name as project_name
    FROM notes n
    LEFT JOIN projects p ON n.project_id = p.id
    WHERE 1=1
  `;
  const params = [];
  if (project_id) { sql += ' AND n.project_id = ?'; params.push(project_id); }
  sql += ' ORDER BY n.date DESC, n.created_at DESC';
  res.json(db.prepare(sql).all(...params));
});

// POST /api/notes
router.post('/', (req, res) => {
  const { title, body = '', project_id = null, date } = req.body;
  if (!title) return res.status(400).json({ error: 'title is required' });
  const noteDate = date || new Date().toISOString().split('T')[0];
  const result = db.prepare('INSERT INTO notes (title, body, project_id, date) VALUES (?, ?, ?, ?)')
    .run(title, body, project_id, noteDate);
  res.json(db.prepare('SELECT n.*, p.name as project_name FROM notes n LEFT JOIN projects p ON n.project_id = p.id WHERE n.id = ?').get(result.lastInsertRowid));
});

// PUT /api/notes/:id
router.put('/:id', (req, res) => {
  const { title, body, project_id, date } = req.body;
  db.prepare('UPDATE notes SET title=?, body=?, project_id=?, date=? WHERE id=?')
    .run(title, body, project_id, date, req.params.id);
  res.json(db.prepare('SELECT n.*, p.name as project_name FROM notes n LEFT JOIN projects p ON n.project_id = p.id WHERE n.id = ?').get(req.params.id));
});

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

module.exports = router;


File created successfully at: /Users/khalidbinkalban/my-workspace/server/routes/notes.js

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

// GET /api/notes
router.get('/', (req, res) => {
  const { project_id } = req.query;
  let sql = `
    SELECT n.*, p.name as project_name
    FROM notes n
    LEFT JOIN projects p ON n.project_id = p.id
    WHERE 1=1
  `;
  const params = [];
  if (project_id) { sql += ' AND n.project_id = ?'; params.push(project_id); }
  sql += ' ORDER BY n.date DESC, n.created_at DESC';
  res.json(db.prepare(sql).all(...params));
});

// POST /api/notes
router.post('/', (req, res) => {
  const { title, body = '', project_id = null, date } = req.body;
  if (!title) return res.status(400).json({ error: 'title is required' });
  const noteDate = date || new Date().toISOString().split('T')[0];
  const result = db.prepare('INSERT INTO notes (title, body, project_id, date) VALUES (?, ?, ?, ?)')
    .run(title, body, project_id, noteDate);
  res.json(db.prepare('SELECT n.*, p.name as project_name FROM notes n LEFT JOIN projects p ON n.project_id = p.id WHERE n.id = ?').get(result.lastInsertRowid));
});

// PUT /api/notes/:id
router.put('/:id', (req, res) => {
  const { title, body, project_id, date } = req.body;
  db.prepare('UPDATE notes SET title=?, body=?, project_id=?, date=? WHERE id=?')
    .run(title, body, project_id, date, req.params.id);
  res.json(db.prepare('SELECT n.*, p.name as project_name FROM notes n LEFT JOIN projects p ON n.project_id = p.id WHERE n.id = ?').get(req.params.id));
});

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

module.exports = router;


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

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

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

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

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

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

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

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

module.exports = router;


File created successfully at: /Users/khalidbinkalban/my-workspace/server/routes/ai.js

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

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

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

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

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

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

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

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

module.exports = router;


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;


File created successfully at: /Users/khalidbinkalban/my-workspace/server/routes/people.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, 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;


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

// GET /api/search?q=
router.get('/', (req, res) => {
  const { q } = req.query;
  if (!q || q.trim().length < 2) return res.json({ tasks: [], projects: [], notes: [] });
  const query = `%${q.trim()}%`;

  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.title LIKE ? OR t.notes LIKE ? OR t.assignee LIKE ?
    ORDER BY t.created_at DESC LIMIT 20
  `).all(query, query, query);

  const projects = db.prepare(`
    SELECT * FROM projects
    WHERE name LIKE ? OR description LIKE ?
    ORDER BY updated_at DESC LIMIT 10
  `).all(query, query);

  const notes = db.prepare(`
    SELECT n.*, p.name as project_name FROM notes n
    LEFT JOIN projects p ON n.project_id = p.id
    WHERE n.title LIKE ? OR n.body LIKE ?
    ORDER BY n.date DESC LIMIT 10
  `).all(query, query);

  res.json({ tasks, projects, notes });
});

module.exports = router;


File created successfully at: /Users/khalidbinkalban/my-workspace/server/routes/search.js

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

// GET /api/search?q=
router.get('/', (req, res) => {
  const { q } = req.query;
  if (!q || q.trim().length < 2) return res.json({ tasks: [], projects: [], notes: [] });
  const query = `%${q.trim()}%`;

  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.title LIKE ? OR t.notes LIKE ? OR t.assignee LIKE ?
    ORDER BY t.created_at DESC LIMIT 20
  `).all(query, query, query);

  const projects = db.prepare(`
    SELECT * FROM projects
    WHERE name LIKE ? OR description LIKE ?
    ORDER BY updated_at DESC LIMIT 10
  `).all(query, query);

  const notes = db.prepare(`
    SELECT n.*, p.name as project_name FROM notes n
    LEFT JOIN projects p ON n.project_id = p.id
    WHERE n.title LIKE ? OR n.body LIKE ?
    ORDER BY n.date DESC LIMIT 10
  `).all(query, query);

  res.json({ tasks, projects, notes });
});

module.exports = router;


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

function getWeekStart() {
  const now = new Date();
  const day = now.getDay(); // 0=Sun, 1=Mon
  const diff = (day === 0 ? -6 : 1 - day);
  const monday = new Date(now);
  monday.setDate(now.getDate() + diff);
  monday.setHours(0, 0, 0, 0);
  return monday.toISOString();
}

function getSevenDaysAgo() {
  const d = new Date();
  d.setDate(d.getDate() - 7);
  return d.toISOString().split('T')[0];
}

function getToday() {
  return new Date().toISOString().split('T')[0];
}

// GET /api/weekly-review
router.get('/', (req, res) => {
  const weekStart = getWeekStart();
  const sevenAgo = getSevenDaysAgo();
  const today = getToday();

  const completedThisWeek = db.prepare(`
    SELECT t.*, p.name as project_name FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.status = 'Done' AND t.completed_at >= ?
    ORDER BY t.completed_at DESC
  `).all(weekStart);

  const addedThisWeek = db.prepare(`
    SELECT t.*, p.name as project_name FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.created_at >= ?
    ORDER BY t.created_at DESC
  `).all(weekStart);

  const overdue = db.prepare(`
    SELECT t.*, p.name as project_name FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.due_date != '' AND t.due_date < ? AND t.status != 'Done'
    ORDER BY t.due_date ASC
  `).all(today);

  const staleFollowUps = db.prepare(`
    SELECT t.*, p.name as project_name FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.is_follow_up = 1 AND t.status != 'Done' AND t.created_at < ?
    ORDER BY t.created_at ASC
  `).all(sevenAgo + 'T00:00:00.000Z');

  const inactiveProjects = db.prepare(`
    SELECT p.*,
      (SELECT COUNT(*) FROM tasks t WHERE t.project_id = p.id AND t.status != 'Done') as open_task_count
    FROM projects p
    WHERE p.status != 'Done' AND p.updated_at < ?
    ORDER BY p.priority, p.name
  `).all(weekStart);

  res.json({ completedThisWeek, addedThisWeek, overdue, staleFollowUps, inactiveProjects, weekStart });
});

// POST /api/weekly-review/reset — clear today focus
router.post('/reset', (req, res) => {
  db.prepare("UPDATE tasks SET is_today_focus = 0, updated_at = datetime('now') WHERE is_today_focus = 1").run();
  res.json({ success: true, message: 'Today focus cleared. Ready for the new week!' });
});

module.exports = router;


File created successfully at: /Users/khalidbinkalban/my-workspace/server/routes/weekly.js

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

function getWeekStart() {
  const now = new Date();
  const day = now.getDay(); // 0=Sun, 1=Mon
  const diff = (day === 0 ? -6 : 1 - day);
  const monday = new Date(now);
  monday.setDate(now.getDate() + diff);
  monday.setHours(0, 0, 0, 0);
  return monday.toISOString();
}

function getSevenDaysAgo() {
  const d = new Date();
  d.setDate(d.getDate() - 7);
  return d.toISOString().split('T')[0];
}

function getToday() {
  return new Date().toISOString().split('T')[0];
}

// GET /api/weekly-review
router.get('/', (req, res) => {
  const weekStart = getWeekStart();
  const sevenAgo = getSevenDaysAgo();
  const today = getToday();

  const completedThisWeek = db.prepare(`
    SELECT t.*, p.name as project_name FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.status = 'Done' AND t.completed_at >= ?
    ORDER BY t.completed_at DESC
  `).all(weekStart);

  const addedThisWeek = db.prepare(`
    SELECT t.*, p.name as project_name FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.created_at >= ?
    ORDER BY t.created_at DESC
  `).all(weekStart);

  const overdue = db.prepare(`
    SELECT t.*, p.name as project_name FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.due_date != '' AND t.due_date < ? AND t.status != 'Done'
    ORDER BY t.due_date ASC
  `).all(today);

  const staleFollowUps = db.prepare(`
    SELECT t.*, p.name as project_name FROM tasks t
    LEFT JOIN projects p ON t.project_id = p.id
    WHERE t.is_follow_up = 1 AND t.status != 'Done' AND t.created_at < ?
    ORDER BY t.created_at ASC
  `).all(sevenAgo + 'T00:00:00.000Z');

  const inactiveProjects = db.prepare(`
    SELECT p.*,
      (SELECT COUNT(*) FROM tasks t WHERE t.project_id = p.id AND t.status != 'Done') as open_task_count
    FROM projects p
    WHERE p.status != 'Done' AND p.updated_at < ?
    ORDER BY p.priority, p.name
  `).all(weekStart);

  res.json({ completedThisWeek, addedThisWeek, overdue, staleFollowUps, inactiveProjects, weekStart });
});

// POST /api/weekly-review/reset — clear today focus
router.post('/reset', (req, res) => {
  db.prepare("UPDATE tasks SET is_today_focus = 0, updated_at = datetime('now') WHERE is_today_focus = 1").run();
  res.json({ success: true, message: 'Today focus cleared. Ready for the new week!' });
});

module.exports = router;


Now the client config files:


<!DOCTYPE html>

    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Workspace</title>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
    <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏢</text></svg>" />


File created successfully at: /Users/khalidbinkalban/my-workspace/client/index.html

<!DOCTYPE html>

    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Workspace</title>
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
    <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏢</text></svg>" />


import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  server: {
    port: 5173,
    proxy: {
      '/api': {
        target: 'http://localhost:3001',
        changeOrigin: true,
      },
    },
  },
  build: {
    outDir: '../dist',
    emptyOutDir: true,
  },
});


File created successfully at: /Users/khalidbinkalban/my-workspace/client/vite.config.js

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  server: {
    port: 5173,
    proxy: {
      '/api': {
        target: 'http://localhost:3001',
        changeOrigin: true,
      },
    },
  },
  build: {
    outDir: '../dist',
    emptyOutDir: true,
  },
});


/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: 'class',
  content: ['./src/**/*.{js,jsx,ts,tsx}', './index.html'],
  theme: {
    extend: {
      fontFamily: {
        sans: ['Inter', 'ui-sans-serif', 'system-ui', 'sans-serif'],
      },
      colors: {
        primary: {
          DEFAULT: '#185FA5',
          light: '#2B7DD4',
          dark: '#0E4178',
        },
      },
      borderRadius: {
        DEFAULT: '8px',
      },
    },
  },
  plugins: [],
};


File created successfully at: /Users/khalidbinkalban/my-workspace/client/tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: 'class',
  content: ['./src/**/*.{js,jsx,ts,tsx}', './index.html'],
  theme: {
    extend: {
      fontFamily: {
        sans: ['Inter', 'ui-sans-serif', 'system-ui', 'sans-serif'],
      },
      colors: {
        primary: {
          DEFAULT: '#185FA5',
          light: '#2B7DD4',
          dark: '#0E4178',
        },
      },
      borderRadius: {
        DEFAULT: '8px',
      },
    },
  },
  plugins: [],
};


module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};


File created successfully at: /Users/khalidbinkalban/my-workspace/client/postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};


@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  * {
    -webkit-tap-highlight-color: transparent;
  }
  body {
    @apply font-sans antialiased bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-100;
  }
  ::-webkit-scrollbar {
    @apply w-1.5 h-1.5;
  }
  ::-webkit-scrollbar-track {
    @apply bg-transparent;
  }
  ::-webkit-scrollbar-thumb {
    @apply bg-gray-300 dark:bg-gray-600 rounded-full;
  }
  ::-webkit-scrollbar-thumb:hover {
    @apply bg-gray-400 dark:bg-gray-500;
  }
}

@layer components {
  .card {
    @apply bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-lg p-4 transition-shadow duration-150;
  }
  .card-hover {
    @apply card hover:shadow-md hover:border-gray-300 dark:hover:border-gray-700 cursor-pointer;
  }
  .btn-primary {
    @apply bg-primary text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-primary-light transition-colors duration-150 disabled:opacity-50 disabled:cursor-not-allowed;
  }
  .btn-secondary {
    @apply bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 px-4 py-2 rounded-lg text-sm font-medium hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-150;
  }
  .btn-ghost {
    @apply text-gray-500 dark:text-gray-400 px-3 py-1.5 rounded-lg text-sm hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-150;
  }
  .btn-danger {
    @apply bg-red-500 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-red-600 transition-colors duration-150;
  }
  .input {
    @apply w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent transition-all duration-150;
  }
  .select {
    @apply input pr-8 appearance-none cursor-pointer;
  }
  .badge {
    @apply inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium;
  }
  .page-header {
    @apply flex items-center justify-between mb-6;
  }
  .page-title {
    @apply text-xl font-semibold text-gray-900 dark:text-white;
  }
  .section-label {
    @apply text-xs font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-3;
  }
  .skeleton {
    @apply animate-pulse bg-gray-200 dark:bg-gray-800 rounded;
  }
}


File created successfully at: /Users/khalidbinkalban/my-workspace/client/src/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  * {
    -webkit-tap-highlight-color: transparent;
  }
  body {
    @apply font-sans antialiased bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-100;
  }
  ::-webkit-scrollbar {
    @apply w-1.5 h-1.5;
  }
  ::-webkit-scrollbar-track {
    @apply bg-transparent;
  }
  ::-webkit-scrollbar-thumb {
    @apply bg-gray-300 dark:bg-gray-600 rounded-full;
  }
  ::-webkit-scrollbar-thumb:hover {
    @apply bg-gray-400 dark:bg-gray-500;
  }
}

@layer components {
  .card {
    @apply bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-lg p-4 transition-shadow duration-150;
  }
  .card-hover {
    @apply card hover:shadow-md hover:border-gray-300 dark:hover:border-gray-700 cursor-pointer;
  }
  .btn-primary {
    @apply bg-primary text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-primary-light transition-colors duration-150 disabled:opacity-50 disabled:cursor-not-allowed;
  }
  .btn-secondary {
    @apply bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 px-4 py-2 rounded-lg text-sm font-medium hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-150;
  }
  .btn-ghost {
    @apply text-gray-500 dark:text-gray-400 px-3 py-1.5 rounded-lg text-sm hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-150;
  }
  .btn-danger {
    @apply bg-red-500 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-red-600 transition-colors duration-150;
  }
  .input {
    @apply w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent transition-all duration-150;
  }
  .select {
    @apply input pr-8 appearance-none cursor-pointer;
  }
  .badge {
    @apply inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium;
  }
  .page-header {
    @apply flex items-center justify-between mb-6;
  }
  .page-title {
    @apply text-xl font-semibold text-gray-900 dark:text-white;
  }
  .section-label {
    @apply text-xs font-semibold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-3;
  }
  .skeleton {
    @apply animate-pulse bg-gray-200 dark:bg-gray-800 rounded;
  }
}


import React from 'react';
import ReactDOM from 'react-dom/client';
import { Toaster } from 'react-hot-toast';
import App from './App.jsx';
import './index.css';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
    <Toaster
      position="top-right"
      toastOptions={{
        duration: 3000,
        style: {
          borderRadius: '8px',
          fontSize: '14px',
        },
      }}
    />
  </React.StrictMode>
);


File created successfully at: /Users/khalidbinkalban/my-workspace/client/src/main.jsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import { Toaster } from 'react-hot-toast';
import App from './App.jsx';
import './index.css';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
    <Toaster
      position="top-right"
      toastOptions={{
        duration: 3000,
        style: {
          borderRadius: '8px',
          fontSize: '14px',
        },
      }}
    />
  </React.StrictMode>
);


const BASE = '/api';

async function request(method, path, body) {
  const opts = {
    method,
    headers: {},
  };
  if (body !== undefined) {
    opts.headers['Content-Type'] = 'application/json';
    opts.body = JSON.stringify(body);
  }
  const res = await fetch(BASE + path, opts);
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
  return data;
}

const api = {
  get:    (path)        => request('GET',    path),
  post:   (path, body)  => request('POST',   path, body),
  put:    (path, body)  => request('PUT',    path, body),
  patch:  (path, body)  => request('PATCH',  path, body),
  delete: (path)        => request('DELETE', path),
};

export default api;


File created successfully at: /Users/khalidbinkalban/my-workspace/client/src/api.js

const BASE = '/api';

async function request(method, path, body) {
  const opts = {
    method,
    headers: {},
  };
  if (body !== undefined) {
    opts.headers['Content-Type'] = 'application/json';
    opts.body = JSON.stringify(body);
  }
  const res = await fetch(BASE + path, opts);
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
  return data;
}

const api = {
  get:    (path)        => request('GET',    path),
  post:   (path, body)  => request('POST',   path, body),
  put:    (path, body)  => request('PUT',    path, body),
  patch:  (path, body)  => request('PATCH',  path, body),
  delete: (path)        => request('DELETE', path),
};

export default api;


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

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

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

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

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

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

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

export function daysAgo(dateStr) {
  if (!dateStr) return 0;
  const diff = Date.now() - new Date(dateStr).getTime();
  return Math.floor(diff / 86400000);
}


File created successfully at: /Users/khalidbinkalban/my-workspace/client/src/utils.js

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

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

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

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

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

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

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

export function daysAgo(dateStr) {
  if (!dateStr) return 0;
  const diff = Date.now() - new Date(dateStr).getTime();
  return Math.floor(diff / 86400000);
}


import { useState, useEffect, createContext, useContext } from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import Sidebar from './components/Sidebar';
import BottomNav from './components/BottomNav';
import Search from './components/Search';
import Today from './components/Today';
import Inbox from './components/Inbox';
import Projects from './components/Projects';
import ProjectDetail from './components/ProjectDetail';
import Tasks from './components/Tasks';
import Notes from './components/Notes';
import FollowUps from './components/FollowUps';
import People from './components/People';
import WeeklyReview from './components/WeeklyReview';

export const AppContext = createContext({});
export const useApp = () => useContext(AppContext);

export default function App() {
  const [darkMode, setDarkMode] = useState(() => localStorage.getItem('darkMode') === 'true');
  const [searchOpen, setSearchOpen] = useState(false);

  useEffect(() => {
    document.documentElement.classList.toggle('dark', darkMode);
    localStorage.setItem('darkMode', darkMode);
  }, [darkMode]);

  useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
        e.preventDefault();
        setSearchOpen(true);
      }
      if (e.key === 'Escape') setSearchOpen(false);
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);

  return (
    <AppContext.Provider value={{ darkMode, setDarkMode, setSearchOpen }}>
      <BrowserRouter>
        <div className="flex h-screen overflow-hidden bg-gray-50 dark:bg-gray-950">
          {/* Desktop sidebar */}
          <Sidebar onSearchOpen={() => setSearchOpen(true)} />

          {/* Main content */}
          <main className="flex-1 overflow-y-auto pb-20 md:pb-0 md:ml-0">
            <Routes>
              <Route path="/" element={<Navigate to="/today" replace />} />
              <Route path="/today" element={<Today />} />
              <Route path="/inbox" element={<Inbox />} />
              <Route path="/projects" element={<Projects />} />
              <Route path="/projects/:id" element={<ProjectDetail />} />
              <Route path="/tasks" element={<Tasks />} />
              <Route path="/notes" element={<Notes />} />
              <Route path="/follow-ups" element={<FollowUps />} />
              <Route path="/people" element={<People />} />
              <Route path="/weekly-review" element={<WeeklyReview />} />
            </Routes>
          </main>

          {/* Mobile bottom nav */}
          <BottomNav />

          {/* Search modal */}
          {searchOpen && <Search onClose={() => setSearchOpen(false)} />}
        </div>
      </BrowserRouter>
    </AppContext.Provider>
  );
}


File created successfully at: /Users/khalidbinkalban/my-workspace/client/src/App.jsx

import { useState, useEffect, createContext, useContext } from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import Sidebar from './components/Sidebar';
import BottomNav from './components/BottomNav';
import Search from './components/Search';
import Today from './components/Today';
import Inbox from './components/Inbox';
import Projects from './components/Projects';
import ProjectDetail from './components/ProjectDetail';
import Tasks from './components/Tasks';
import Notes from './components/Notes';
import FollowUps from './components/FollowUps';
import People from './components/People';
import WeeklyReview from './components/WeeklyReview';

export const AppContext = createContext({});
export const useApp = () => useContext(AppContext);

export default function App() {
  const [darkMode, setDarkMode] = useState(() => localStorage.getItem('darkMode') === 'true');
  const [searchOpen, setSearchOpen] = useState(false);

  useEffect(() => {
    document.documentElement.classList.toggle('dark', darkMode);
    localStorage.setItem('darkMode', darkMode);
  }, [darkMode]);

  useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
        e.preventDefault();
        setSearchOpen(true);
      }
      if (e.key === 'Escape') setSearchOpen(false);
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);

  return (
    <AppContext.Provider value={{ darkMode, setDarkMode, setSearchOpen }}>
      <BrowserRouter>
        <div className="flex h-screen overflow-hidden bg-gray-50 dark:bg-gray-950">
          {/* Desktop sidebar */}
          <Sidebar onSearchOpen={() => setSearchOpen(true)} />

          {/* Main content */}
          <main className="flex-1 overflow-y-auto pb-20 md:pb-0 md:ml-0">
            <Routes>
              <Route path="/" element={<Navigate to="/today" replace />} />
              <Route path="/today" element={<Today />} />
              <Route path="/inbox" element={<Inbox />} />
              <Route path="/projects" element={<Projects />} />
              <Route path="/projects/:id" element={<ProjectDetail />} />
              <Route path="/tasks" element={<Tasks />} />
              <Route path="/notes" element={<Notes />} />
              <Route path="/follow-ups" element={<FollowUps />} />
              <Route path="/people" element={<People />} />
              <Route path="/weekly-review" element={<WeeklyReview />} />
            </Routes>
          </main>

          {/* Mobile bottom nav */}
          <BottomNav />

          {/* Search modal */}
          {searchOpen && <Search onClose={() => setSearchOpen(false)} />}
        </div>
      </BrowserRouter>
    </AppContext.Provider>
  );
}


import { NavLink } from 'react-router-dom';
import {
  CalendarDays, Inbox, FolderKanban, CheckSquare, FileText,
  Bell, Users, BarChart2, Search, Moon, Sun, Zap
} from 'lucide-react';
import { useApp } from '../App';

const NAV = [
  { to: '/today',        icon: CalendarDays, label: 'Today'        },
  { to: '/inbox',        icon: Inbox,        label: 'Inbox'        },
  { to: '/projects',     icon: FolderKanban, label: 'Projects'     },
  { to: '/tasks',        icon: CheckSquare,  label: 'Tasks'        },
  { to: '/notes',        icon: FileText,     label: 'Notes'        },
  { to: '/follow-ups',   icon: Bell,         label: 'Follow-ups'   },
  { to: '/people',       icon: Users,        label: 'People'       },
  { to: '/weekly-review',icon: BarChart2,    label: 'Weekly Review'},
];

export default function Sidebar({ onSearchOpen }) {
  const { darkMode, setDarkMode } = useApp();

  return (

      {/* Logo */}

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

My Workspace

DEWA Senior Manager

      {/* Search shortcut */}
      <button
        onClick={onSearchOpen}
        className="mx-3 mt-3 flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-sm hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors group"
      >
        <Search size={14} />
        Search…
        ⌘K
      </button>

      {/* Nav */}

        {NAV.map(({ to, icon: Icon, label }) => (
          <NavLink
            key={to}
            to={to}
            className={({ isActive }) =>
              `flex items-center gap-2.5 px-3 py-2 rounded-lg text-sm transition-colors duration-100 ${
                isActive
                  ? 'bg-primary/10 text-primary dark:text-primary-light font-medium'
                  : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white'
              }`
            }
          >
            <Icon size={16} />
            {label}
          </NavLink>
        ))}

      {/* Dark mode toggle */}

        <button
          onClick={() => setDarkMode(!darkMode)}
          className="flex items-center gap-2 w-full px-3 py-2 rounded-lg text-sm text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
        >
          {darkMode ? <Sun size={15} /> : <Moon size={15} />}
          {darkMode ? 'Light mode' : 'Dark mode'}
        </button>

  );
}


File created successfully at: /Users/khalidbinkalban/my-workspace/client/src/components/Sidebar.jsx

import { NavLink } from 'react-router-dom';
import {
  CalendarDays, Inbox, FolderKanban, CheckSquare, FileText,
  Bell, Users, BarChart2, Search, Moon, Sun, Zap
} from 'lucide-react';
import { useApp } from '../App';

const NAV = [
  { to: '/today',        icon: CalendarDays, label: 'Today'        },
  { to: '/inbox',        icon: Inbox,        label: 'Inbox'        },
  { to: '/projects',     icon: FolderKanban, label: 'Projects'     },
  { to: '/tasks',        icon: CheckSquare,  label: 'Tasks'        },
  { to: '/notes',        icon: FileText,     label: 'Notes'        },
  { to: '/follow-ups',   icon: Bell,         label: 'Follow-ups'   },
  { to: '/people',       icon: Users,        label: 'People'       },
  { to: '/weekly-review',icon: BarChart2,    label: 'Weekly Review'},
];

export default function Sidebar({ onSearchOpen }) {
  const { darkMode, setDarkMode } = useApp();

  return (

      {/* Logo */}

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

My Workspace

DEWA Senior Manager

      {/* Search shortcut */}
      <button
        onClick={onSearchOpen}
        className="mx-3 mt-3 flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 text-sm hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors group"
      >
        <Search size={14} />
        Search…
        ⌘K
      </button>

      {/* Nav */}

        {NAV.map(({ to, icon: Icon, label }) => (
          <NavLink
            key={to}
            to={to}
            className={({ isActive }) =>
              `flex items-center gap-2.5 px-3 py-2 rounded-lg text-sm transition-colors duration-100 ${
                isActive
                  ? 'bg-primary/10 text-primary dark:text-primary-light font-medium'
                  : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white'
              }`
            }
          >
            <Icon size={16} />
            {label}
          </NavLink>
        ))}

      {/* Dark mode toggle */}

        <button
          onClick={() => setDarkMode(!darkMode)}
          className="flex items-center gap-2 w-full px-3 py-2 rounded-lg text-sm text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
        >
          {darkMode ? <Sun size={15} /> : <Moon size={15} />}
          {darkMode ? 'Light mode' : 'Dark mode'}
        </button>

  );
}


import { useState } from 'react';
import { NavLink } from 'react-router-dom';
import { CalendarDays, Inbox, FolderKanban, CheckSquare, MoreHorizontal, X, FileText, Bell, Users, BarChart2 } from 'lucide-react';

const MAIN = [
  { to: '/today',    icon: CalendarDays, label: 'Today'    },
  { to: '/inbox',    icon: Inbox,        label: 'Inbox'    },
  { to: '/projects', icon: FolderKanban, label: 'Projects' },
  { to: '/tasks',    icon: CheckSquare,  label: 'Tasks'    },
];

const MORE = [
  { to: '/notes',        icon: FileText,  label: 'Notes'        },
  { to: '/follow-ups',   icon: Bell,      label: 'Follow-ups'   },
  { to: '/people',       icon: Users,     label: 'People'       },
  { to: '/weekly-review',icon: BarChart2, label: 'Weekly Review'},
];

export default function BottomNav() {
  const [moreOpen, setMoreOpen] = useState(false);

  return (
    <>
      {/* More drawer */}
      {moreOpen && (

 setMoreOpen(false)}>

 e.stopPropagation()}
          >

              {MORE.map(({ to, icon: Icon, label }) => (
                <NavLink
                  key={to}
                  to={to}
                  onClick={() => setMoreOpen(false)}
                  className={({ isActive }) =>
                    `flex flex-col items-center gap-1 py-2 px-1 rounded-lg text-center ${
                      isActive ? 'text-primary' : 'text-gray-500 dark:text-gray-400'
                    }`
                  }
                >
                  <Icon size={20} />
                  {label}
                </NavLink>
              ))}

      )}

      {/* Bottom bar */}

        {MAIN.map(({ to, icon: Icon, label }) => (
          <NavLink
            key={to}
            to={to}
            className={({ isActive }) =>
              `flex-1 flex flex-col items-center justify-center py-2 gap-1 text-[10px] font-medium transition-colors ${
                isActive ? 'text-primary' : 'text-gray-500 dark:text-gray-400'
              }`
            }
          >
            <Icon size={20} />
            {label}
          </NavLink>
        ))}
        <button
          onClick={() => setMoreOpen(!moreOpen)}
          className={`flex-1 flex flex-col items-center justify-center py-2 gap-1 text-[10px] font-medium transition-colors ${
            moreOpen ? 'text-primary' : 'text-gray-500 dark:text-gray-400'
          }`}
        >
          {moreOpen ? <X size={20} /> : <MoreHorizontal size={20} />}
          More
        </button>

    </>
  );
}


File created successfully at: /Users/khalidbinkalban/my-workspace/client/src/components/BottomNav.jsx

import { useState } from 'react';
import { NavLink } from 'react-router-dom';
import { CalendarDays, Inbox, FolderKanban, CheckSquare, MoreHorizontal, X, FileText, Bell, Users, BarChart2 } from 'lucide-react';

const MAIN = [
  { to: '/today',    icon: CalendarDays, label: 'Today'    },
  { to: '/inbox',    icon: Inbox,        label: 'Inbox'    },
  { to: '/projects', icon: FolderKanban, label: 'Projects' },
  { to: '/tasks',    icon: CheckSquare,  label: 'Tasks'    },
];

const MORE = [
  { to: '/notes',        icon: FileText,  label: 'Notes'        },
  { to: '/follow-ups',   icon: Bell,      label: 'Follow-ups'   },
  { to: '/people',       icon: Users,     label: 'People'       },
  { to: '/weekly-review',icon: BarChart2, label: 'Weekly Review'},
];

export default function BottomNav() {
  const [moreOpen, setMoreOpen] = useState(false);

  return (
    <>
      {/* More drawer */}
      {moreOpen && (

 setMoreOpen(false)}>

 e.stopPropagation()}
          >

              {MORE.map(({ to, icon: Icon, label }) => (
                <NavLink
                  key={to}
                  to={to}
                  onClick={() => setMoreOpen(false)}
                  className={({ isActive }) =>
                    `flex flex-col items-center gap-1 py-2 px-1 rounded-lg text-center ${
                      isActive ? 'text-primary' : 'text-gray-500 dark:text-gray-400'
                    }`
                  }
                >
                  <Icon size={20} />
                  {label}
                </NavLink>
              ))}

      )}

      {/* Bottom bar */}

        {MAIN.map(({ to, icon: Icon, label }) => (
          <NavLink
            key={to}
            to={to}
            className={({ isActive }) =>
              `flex-1 flex flex-col items-center justify-center py-2 gap-1 text-[10px] font-medium transition-colors ${
                isActive ? 'text-primary' : 'text-gray-500 dark:text-gray-400'
              }`
            }
          >
            <Icon size={20} />
            {label}
          </NavLink>
        ))}
        <button
          onClick={() => setMoreOpen(!moreOpen)}
          className={`flex-1 flex flex-col items-center justify-center py-2 gap-1 text-[10px] font-medium transition-colors ${
            moreOpen ? 'text-primary' : 'text-gray-500 dark:text-gray-400'
          }`}
        >
          {moreOpen ? <X size={20} /> : <MoreHorizontal size={20} />}
          More
        </button>

    </>
  );
}


import { AlertTriangle } from 'lucide-react';

export default function ConfirmDialog({ title, message, onConfirm, onCancel, danger = true }) {
  return (

 e.stopPropagation()}
      >

          {danger && (

              <AlertTriangle size={18} className="text-red-600 dark:text-red-400" />

          )}

{title}

            {message &&
{message}
}

          <button onClick={onCancel} className="btn-secondary">Cancel</button>
          <button onClick={onConfirm} className={danger ? 'btn-danger' : 'btn-primary'}>Delete</button>

  );
}


File created successfully at: /Users/khalidbinkalban/my-workspace/client/src/components/ConfirmDialog.jsx

import { AlertTriangle } from 'lucide-react';

export default function ConfirmDialog({ title, message, onConfirm, onCancel, danger = true }) {
  return (

 e.stopPropagation()}
      >

          {danger && (

              <AlertTriangle size={18} className="text-red-600 dark:text-red-400" />

          )}

{title}

            {message &&
{message}
}

          <button onClick={onCancel} className="btn-secondary">Cancel</button>
          <button onClick={onConfirm} className={danger ? 'btn-danger' : 'btn-primary'}>Delete</button>

  );
}