/* The board: seven pipeline columns, drag a card to move it, filter by person,
   client, type and a date window. Also a list view for when you want the whole
   pipeline in one table. */

const STATUS_ORDER = [
  'AD ASSIGNED',
  'AD IN PROGRESS',
  'AD SENT INTERNALLY',
  'AD SENT TO CLIENT',
  'AD REVISION',
  'AD CONFIRMED',
  'AD PUBLISHED',
];

const DATE_FIELDS = [
  { value: 'due_date', label: 'Deadline' },
  { value: 'created_at', label: 'Created' },
  { value: 'delivery_month', label: 'Delivery month' },
];

const DATE_PRESETS = [
  { value: '', label: 'Any date' },
  { value: 'overdue', label: 'Overdue' },
  { value: 'today', label: 'Today' },
  { value: 'week', label: 'This week' },
  { value: 'next7', label: 'Next 7 days' },
  { value: 'month', label: 'This month' },
  { value: 'last30', label: 'Last 30 days' },
  { value: 'custom', label: 'Custom range…' },
];

/* Il percorso standard su Drive, ricalcolato mentre compili il form. Lo stesso
   calcolo che fa il server, così quello che leggi è quello che verrà usato. */
function drivePath(form) {
  const m = (form.delivery_month || '').slice(0, 7) || isoMonth(new Date());
  if (form.type === 'static') return `Internal / ${m} / Static ads`;
  const a = (form.angle || '').trim();
  const leaf = a ? a[0].toUpperCase() + a.slice(1).toLowerCase() : 'General';
  return `Internal / ${m} / ${leaf}`;
}

function AvatarStack({ users }) {
  if (!users.length) return <Avatar user={null} />;
  return (
    <div className="avatar-stack">
      {users.slice(0, 3).map((u) => <Avatar key={u.id} user={u} />)}
      {users.length > 3 && <div className="avatar more">+{users.length - 3}</div>}
    </div>
  );
}

function TaskCard({ task, client, assignees, onOpen, onDragStart, dragging }) {
  const due = dueLabel(task.due_date);
  return (
    <div
      className={'task-card' + (dragging ? ' dragging' : '')}
      draggable
      onDragStart={(e) => onDragStart(e, task)}
      onClick={() => onOpen(task.id)}
    >
      <span className="client-bar" style={{ background: (client && client.color) || 'var(--border-strong)' }} />
      {/* The brand comes first: on a mixed board you look for the client before
          you look for the ad. */}
      <div className="card-client" style={{ color: (client && client.color) || 'var(--text-secondary)' }}>
        <Icon name={task.type === 'video' ? 'video' : 'image'} size={11} />
        {client ? client.name : 'No client'}
      </div>
      <div className="title">{task.title}</div>
      <div className="meta">
        {task.quantity > 1 && <span className="chip">×{task.quantity} ads</span>}
        {task.effort_days > 0 && (
          <span className="chip" title="Editing days">
            {task.effort_days}{task.effort_days === 1 ? ' day' : ' days'}
          </span>
        )}
        {task.angle && <span className="chip">{task.angle}</span>}
        {task.format && <span className="chip">{task.format}</span>}
        {task.version_count > 0 && <span className="chip blue">v{task.version_count}</span>}
        {task.revision_rounds > 0 && <span className="chip amber">R{task.revision_rounds}</span>}
        {task.open_comments > 0 && (
          <span className="chip amber"><Icon name="comment" size={11} />{task.open_comments}</span>
        )}
        {due && <span className={'chip ' + due.tone}><Icon name="clock" size={11} />{due.text}</span>}
        {task.delivery_url && <span className="chip green"><Icon name="check" size={11} />delivered</span>}
        <span className="spacer" />
        <AvatarStack users={assignees} />
      </div>
    </div>
  );
}

/* Month grid keyed on the deadline. The point is to see the week ahead the way
   the team feels it, not as a list. */
function CalendarView({ month, tasks, directory, onMove, go, onMonth }) {
  const [dropDay, setDropDay] = React.useState(null);
  const first = new Date(month + '-01T00:00:00');
  const startOffset = (first.getDay() + 6) % 7; // Monday-first weeks
  const daysInMonth = new Date(first.getFullYear(), first.getMonth() + 1, 0).getDate();
  const cells = [];
  for (let i = 0; i < startOffset; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) {
    cells.push(new Date(first.getFullYear(), first.getMonth(), d));
  }
  while (cells.length % 7) cells.push(null);

  const byDay = {};
  tasks.forEach((t) => {
    if (!t.due_date) return;
    (byDay[t.due_date] = byDay[t.due_date] || []).push(t);
  });
  const undated = tasks.filter((t) => !t.due_date);
  const todayIso = iso(new Date());
  const shift = (n) => {
    const d = new Date(first.getFullYear(), first.getMonth() + n, 1);
    onMonth(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`);
  };

  return (
    <>
      <div className="row" style={{ marginBottom: 12 }}>
        <button className="btn sm" onClick={() => shift(-1)}>←</button>
        <strong style={{ minWidth: 170, textAlign: 'center' }}>
          {first.toLocaleDateString('en-GB', { month: 'long', year: 'numeric' })}
        </strong>
        <button className="btn sm" onClick={() => shift(1)}>→</button>
        <button className="btn sm" onClick={() => onMonth(isoMonth(new Date()))}>Today</button>
        <span className="spacer" />
        <span className="small muted">Drag a task to another day to move its deadline</span>
      </div>

      <div className="calendar">
        {['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'].map((d) => (
          <div key={d} className="cal-head">{d}</div>
        ))}
        {cells.map((day, i) => {
          if (!day) return <div key={'x' + i} className="cal-cell empty" />;
          const key = iso(day);
          const items = byDay[key] || [];
          const weekend = day.getDay() === 0 || day.getDay() === 6;
          return (
            <div
              key={key}
              className={'cal-cell' + (weekend ? ' weekend' : '') + (key === todayIso ? ' today' : '') + (dropDay === key ? ' drag-over' : '')}
              onDragOver={(e) => { e.preventDefault(); setDropDay(key); }}
              onDragLeave={() => setDropDay((v) => (v === key ? null : v))}
              onDrop={(e) => {
                e.preventDefault();
                setDropDay(null);
                onMove(e.dataTransfer.getData('text/plain'), key);
              }}
            >
              <div className="cal-day">{day.getDate()}</div>
              {items.map((t) => {
                const client = directory.byId[t.client_id];
                return (
                  <div
                    key={t.id}
                    className="cal-task"
                    draggable
                    onDragStart={(e) => e.dataTransfer.setData('text/plain', t.id)}
                    onClick={() => go('task', t.id)}
                    title={`${client ? client.name : ''} · ${t.title} · ${STATUS_SHORT[t.status]}`}
                    style={{ borderLeftColor: (client && client.color) || 'var(--border-strong)' }}
                  >
                    <span className="cal-task-client" style={{ color: (client && client.color) || 'var(--text-secondary)' }}>
                      <span className="dot" style={{ background: STATUS_COLORS[t.status] }} />
                      {client ? client.name : 'No client'}
                    </span>
                    <span className="cal-task-title">{t.title}</span>
                  </div>
                );
              })}
            </div>
          );
        })}
      </div>

      {undated.length > 0 && (
        <div className="card card-pad" style={{ marginTop: 16 }}>
          <div className="row" style={{ marginBottom: 8 }}>
            <strong>No deadline</strong>
            <span className="chip">{undated.length}</span>
          </div>
          <div className="row wrap" style={{ gap: 6 }}>
            {undated.map((t) => {
              const client = directory.byId[t.client_id];
              return (
                <div key={t.id} className="cal-task" draggable
                     onDragStart={(e) => e.dataTransfer.setData('text/plain', t.id)}
                     onClick={() => go('task', t.id)}
                     style={{ borderLeftColor: (client && client.color) || 'var(--border-strong)', maxWidth: 260 }}>
                  <span className="cal-task-client" style={{ color: (client && client.color) || 'var(--text-secondary)' }}>
                    <span className="dot" style={{ background: STATUS_COLORS[t.status] }} />
                    {client ? client.name : 'No client'}
                  </span>
                  <span className="cal-task-title">{t.title}</span>
                </div>
              );
            })}
          </div>
        </div>
      )}
    </>
  );
}

function AssigneePicker({ users, value, onChange }) {
  return (
    <div className="picker">
      {users.map((u) => {
        const on = value.includes(u.id);
        return (
          <label key={u.id} className={'picker-row' + (on ? ' on' : '')}>
            <input
              type="checkbox"
              checked={on}
              onChange={() => onChange(on ? value.filter((id) => id !== u.id) : [...value, u.id])}
            />
            <Avatar user={u} />
            <span>{u.name || u.email}</span>
            <span className="spacer" />
            <span className="small muted">{u.job_title || u.role}</span>
          </label>
        );
      })}
    </div>
  );
}

function TaskForm({ form, set, clients, users, creating }) {
  const isStatic = form.type === 'static';
  return (
    <>
      <div className="row" style={{ gap: 14 }}>
        <div className="field" style={{ flex: 1 }}>
          <label className="label">Client</label>
          <select className="select" value={form.client_id} onChange={(e) => set('client_id', e.target.value)}>
            {clients.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
          </select>
        </div>
        <div className="field" style={{ flex: 1 }}>
          <label className="label">Type</label>
          <div className="segmented" style={{ width: '100%' }}>
            <button className={!isStatic ? 'active' : ''} style={{ flex: 1 }} onClick={() => set('type', 'video')}>Video ad</button>
            <button className={isStatic ? 'active' : ''} style={{ flex: 1 }} onClick={() => set('type', 'static')}>Static ad</button>
          </div>
        </div>
      </div>

      <div className="field">
        <label className="label">Task name</label>
        <input className="input" value={form.title} autoFocus={creating}
               placeholder="e.g. Caviar at half the price"
               onChange={(e) => set('title', e.target.value)} />
      </div>

      {!isStatic && (
        <div className="field">
          <label className="label">Length — this is what the editor's day costs</label>
          <div className="segmented" style={{ width: '100%' }}>
            <button style={{ flex: 1 }} className={(form.video_length || 'short') === 'short' ? 'active' : ''}
                    onClick={() => set('video_length', 'short')}>
              Short · half a day each
            </button>
            <button style={{ flex: 1 }} className={form.video_length === 'long' ? 'active' : ''}
                    onClick={() => set('video_length', 'long')}>
              Long · a full day each
            </button>
          </div>
          <p className="small muted" style={{ marginTop: 6, marginBottom: 0 }}>
            {(() => {
              const q = Math.max(1, Number(form.quantity) || 1);
              const d = q * ((form.video_length || 'short') === 'long' ? 1 : 0.5);
              return `${q} ${q === 1 ? 'ad' : 'ads'} = ${d} ${d === 1 ? 'day' : 'days'} of editing`;
            })()}
          </p>
        </div>
      )}

      <div className="row" style={{ gap: 14 }}>
        <div className="field" style={{ width: 110 }}>
          <label className="label">How many ads</label>
          <input className="input" type="number" min="1" value={form.quantity || 1}
                 onChange={(e) => set('quantity', Math.max(1, Number(e.target.value) || 1))} />
        </div>
        <div className="field" style={{ flex: 1 }}>
          <label className="label">Angle</label>
          <input className="input" value={form.angle || ''} placeholder="e.g. UGC testimonial"
                 onChange={(e) => set('angle', e.target.value)} />
        </div>
        <div className="field" style={{ flex: 1 }}>
          <label className="label">Format</label>
          <select className="select" value={form.format || ''} onChange={(e) => set('format', e.target.value)}>
            <option value="">Not set</option>
            {FORMATS.map((f) => <option key={f} value={f}>{f}</option>)}
          </select>
        </div>
      </div>

      <div className="row" style={{ gap: 14 }}>
        <div className="field" style={{ flex: 1 }}>
          <label className="label">Deadline</label>
          <input className="input" type="date" value={form.due_date || ''}
                 onChange={(e) => set('due_date', e.target.value)} />
        </div>
        <div className="field" style={{ flex: 1 }}>
          <label className="label">Delivery month</label>
          <input className="input" type="month" value={(form.delivery_month || '').slice(0, 7)}
                 onChange={(e) => set('delivery_month', e.target.value ? e.target.value + '-01' : null)} />
        </div>
      </div>

      <div className="field">
        <label className="label">Assigned to</label>
        <AssigneePicker
          users={users.filter((u) => u.is_active && u.role !== 'client')}
          value={form.assignee_ids || []}
          onChange={(v) => set('assignee_ids', v)}
        />
      </div>

      <div className="field">
        <label className="label">Instructions</label>
        <textarea className="input" value={form.description || ''}
                  placeholder="What to make, references, must-haves…"
                  onChange={(e) => set('description', e.target.value)} />
      </div>

      <div className="field">
        <label className="label">Script / copy</label>
        <textarea className="input" value={form.script || ''}
                  placeholder="The words on screen or in the voiceover"
                  onChange={(e) => set('script', e.target.value)} />
      </div>

      <div className="field">
        <label className="label">Source material</label>
        <input className="input" value={form.source_url || ''}
               placeholder="Drive folder with the raw footage or product shots"
               onChange={(e) => set('source_url', e.target.value)} />
      </div>

      <div className="field">
        <label className="label">Cartella su Drive</label>
        {form.drive_target_folder_url ? (
          <div className="row">
            <input className="input" value={form.drive_target_folder_url}
                   onChange={(e) => set('drive_target_folder_url', e.target.value)} />
            <button className="btn sm" onClick={() => set('drive_target_folder_url', '')}>
              Torna allo standard
            </button>
          </div>
        ) : (
          <div className="row">
            <span className="chip blue" style={{ fontFamily: 'var(--mono)' }}>{drivePath(form)}</span>
            <button className="btn sm" onClick={() => set('drive_target_folder_url', ' ')}>
              Cambia cartella
            </button>
          </div>
        )}
        <p className="small muted" style={{ marginTop: 6, marginBottom: 0 }}>
          Di standard segue angle e mese di delivery e si aggiorna da solo. Incolla il link
          di un'altra cartella se questo set va altrove, tipo Summer Sale.
        </p>
      </div>

      <div className="field">
        <label className="label">{isStatic ? 'Figma link' : 'Delivery link (Drive)'}</label>
        <input className="input" value={form.delivery_url || ''}
               placeholder={isStatic ? 'https://figma.com/design/…' : 'Filled automatically when the ad is confirmed'}
               onChange={(e) => set('delivery_url', e.target.value)} />
      </div>
    </>
  );
}

function NewTaskModal({ clients, users, onClose, onCreated }) {
  const thisMonth = isoMonth(new Date()) + '-01';
  const [form, setForm] = React.useState({
    client_id: clients[0] ? clients[0].id : '',
    title: '', type: 'video', angle: '', format: '', quantity: 1, video_length: 'short', assignee_ids: [],
    delivery_url: '', source_url: '', due_date: '', delivery_month: thisMonth, drive_target_folder_url: '',
    description: '', script: '',
  });
  const [saving, setSaving] = React.useState(false);
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  const save = () => {
    if (!form.title.trim() || !form.client_id) { toast('Client and task name are required', 'error'); return; }
    setSaving(true);
    api.post('/api/tasks', {
      ...form,
      due_date: form.due_date || null,
      delivery_month: form.delivery_month || null,
      angle: form.angle || null,
      format: form.format || null,
      quantity: Math.max(1, Number(form.quantity) || 1),
      video_length: form.type === 'video' ? (form.video_length || 'short') : null,
      delivery_url: form.delivery_url || null,
      source_url: form.source_url || null,
      drive_target_folder_url: (form.drive_target_folder_url || '').trim() || null,
    })
      .then((t) => { toast('Task created'); onCreated(t); onClose(); })
      .catch((e) => toast(e.message, 'error'))
      .finally(() => setSaving(false));
  };

  return (
    <Modal
      title="New task"
      width={560}
      onClose={onClose}
      footer={<>
        <button className="btn" onClick={onClose}>Cancel</button>
        <button className="btn primary" disabled={saving} onClick={save}>{saving ? 'Creating…' : 'Create task'}</button>
      </>}
    >
      <TaskForm form={form} set={set} clients={clients} users={users} creating />
      <p className="small muted" style={{ margin: 0 }}>
        The angle and the delivery month become the Drive folders when the ad is confirmed.
      </p>
    </Modal>
  );
}

function Board({ go, me, directory, mine }) {
  const [tasks, setTasks] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [creating, setCreating] = React.useState(false);
  const [dragId, setDragId] = React.useState(null);
  const [overStatus, setOverStatus] = React.useState(null);
  const saved = JSON.parse(localStorage.getItem('studio.filters') || '{}');
  const [calMonth, setCalMonth] = React.useState(isoMonth(new Date()));
  const [filters, setFilters] = React.useState({
    client_id: saved.client_id || '',
    assignee_id: mine ? me.id : (saved.assignee_id || ''),
    type: saved.type || '',
    status: saved.status || '',
    view: saved.view || 'board',
    hidePublished: saved.hidePublished !== false,
    date_field: saved.date_field || 'due_date',
    date_preset: saved.date_preset || '',
    date_from: saved.date_from || '',
    date_to: saved.date_to || '',
  });

  const setFilter = (k, v) => {
    const next = { ...filters, [k]: v };
    // Picking a preset fills the window; "Custom range…" leaves the two inputs
    // to the user.
    if (k === 'date_preset' && v !== 'custom') {
      const range = dateRange(v);
      next.date_from = range.from;
      next.date_to = range.to;
    }
    setFilters(next);
    const { assignee_id, ...rest } = next;
    localStorage.setItem('studio.filters', JSON.stringify(mine ? rest : next));
  };

  // Filters live in localStorage, so a window set days ago can quietly hide
  // everything. This is the way back.
  const clearFilters = () => {
    const reset = {
      client_id: '', assignee_id: mine ? me.id : '', type: '', status: '',
      view: filters.view, hidePublished: false,
      date_field: 'due_date', date_preset: '', date_from: '', date_to: '',
    };
    setFilters(reset);
    localStorage.removeItem('studio.filters');
  };

  const load = React.useCallback(() => {
    const qs = new URLSearchParams();
    if (filters.client_id) qs.set('client_id', filters.client_id);
    if (mine) qs.set('assignee_id', me.id);
    else if (filters.assignee_id) qs.set('assignee_id', filters.assignee_id);
    if (filters.type) qs.set('type', filters.type);
    if (filters.view === 'calendar') {
      // The visible month IS the window; the date filter would fight it. Undated
      // tasks come along too and get their own tray under the grid.
      const first = new Date(calMonth + '-01T00:00:00');
      const last = new Date(first.getFullYear(), first.getMonth() + 1, 0);
      qs.set('date_field', 'due_date');
      qs.set('date_from', iso(first));
      qs.set('date_to', iso(last));
      qs.set('include_undated', 'true');
    } else if (filters.date_from || filters.date_to) {
      qs.set('date_field', filters.date_field);
      if (filters.date_from) qs.set('date_from', filters.date_from);
      if (filters.date_to) qs.set('date_to', filters.date_to);
    }
    setLoading(true);
    api.get('/api/tasks?' + qs.toString())
      .then((d) => setTasks(d.items))
      .catch((e) => toast(e.message, 'error'))
      .finally(() => setLoading(false));
  }, [filters.client_id, filters.assignee_id, filters.type, filters.date_field,
      filters.date_from, filters.date_to, filters.view, calMonth, mine, me.id]);

  React.useEffect(load, [load]);

  const columns = React.useMemo(() => {
    let visible = filters.hidePublished
      ? STATUS_ORDER.filter((s) => s !== 'AD PUBLISHED')
      : STATUS_ORDER;
    if (filters.status) visible = [filters.status];
    return visible.map((status) => ({ status, items: tasks.filter((t) => t.status === status) }));
  }, [tasks, filters.hidePublished, filters.status]);

  const listed = React.useMemo(() => {
    if (filters.status) return tasks.filter((t) => t.status === filters.status);
    if (filters.hidePublished) return tasks.filter((t) => t.status !== 'AD PUBLISHED');
    return tasks;
  }, [tasks, filters.status, filters.hidePublished]);

  const move = (task, status) => {
    if (task.status === status) return;
    const previous = tasks;
    setTasks((list) => list.map((t) => (t.id === task.id ? { ...t, status } : t)));
    api.post(`/api/tasks/${task.id}/status`, { status })
      .then(() => { if (status === 'AD CONFIRMED') toast('Confirmed — publishing to Drive'); })
      .catch((e) => { toast(e.message, 'error'); setTasks(previous); });
  };

  const onDragStart = (e, task) => {
    setDragId(task.id);
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', task.id);
  };

  const clientOf = (t) => directory.byId[t.client_id];
  const peopleOf = (t) => (t.assignee_ids || []).map((id) => directory.byId[id]).filter(Boolean);

  return (
    <>
      <div className="filters">
        <select className="select" value={filters.client_id} onChange={(e) => setFilter('client_id', e.target.value)}>
          <option value="">All clients</option>
          {directory.clients.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
        </select>

        {!mine && (
          <select className="select" value={filters.assignee_id} onChange={(e) => setFilter('assignee_id', e.target.value)}>
            <option value="">Everyone</option>
            <option value="unassigned">Unassigned</option>
            {directory.users.filter((u) => u.is_active && u.role !== 'client').map((u) => (
              <option key={u.id} value={u.id}>{u.name || u.email}</option>
            ))}
          </select>
        )}

        <div className="segmented">
          <button className={filters.type === '' ? 'active' : ''} onClick={() => setFilter('type', '')}>All</button>
          <button className={filters.type === 'video' ? 'active' : ''} onClick={() => setFilter('type', 'video')}>Video</button>
          <button className={filters.type === 'static' ? 'active' : ''} onClick={() => setFilter('type', 'static')}>Static</button>
        </div>

        <select className="select" value={filters.date_field} onChange={(e) => setFilter('date_field', e.target.value)}>
          {DATE_FIELDS.map((f) => <option key={f.value} value={f.value}>{f.label}</option>)}
        </select>
        <select className="select" value={filters.date_preset} onChange={(e) => setFilter('date_preset', e.target.value)}>
          {DATE_PRESETS.map((p) => <option key={p.value} value={p.value}>{p.label}</option>)}
        </select>
        {filters.date_preset === 'custom' && (
          <>
            <input className="input" type="date" style={{ width: 150 }} value={filters.date_from}
                   onChange={(e) => setFilter('date_from', e.target.value)} />
            <span className="small muted">to</span>
            <input className="input" type="date" style={{ width: 150 }} value={filters.date_to}
                   onChange={(e) => setFilter('date_to', e.target.value)} />
          </>
        )}

        <span className="spacer" />

        {filters.status && (
          <button className="btn sm" onClick={() => setFilter('status', '')}>
            <Icon name="x" size={13} />{STATUS_SHORT[filters.status]}
          </button>
        )}

        <label className="row small muted" style={{ gap: 6, cursor: 'pointer' }}>
          <input type="checkbox" checked={!filters.hidePublished}
                 onChange={(e) => setFilter('hidePublished', !e.target.checked)} />
          Show published
        </label>

        <div className="segmented">
          <button className={filters.view === 'board' ? 'active' : ''} title="Board"
                  onClick={() => setFilter('view', 'board')}>
            <Icon name="board" size={13} />
          </button>
          <button className={filters.view === 'list' ? 'active' : ''} title="List"
                  onClick={() => setFilter('view', 'list')}>
            <Icon name="list" size={13} />
          </button>
          <button className={filters.view === 'calendar' ? 'active' : ''} title="Calendar"
                  onClick={() => setFilter('view', 'calendar')}>
            <Icon name="calendar" size={13} />
          </button>
        </div>

        {me.role === 'admin' && (
          <button className="btn primary" onClick={() => setCreating(true)}>
            <Icon name="plus" size={15} />New task
          </button>
        )}
      </div>

      {!loading && filters.view !== 'calendar' && (
        <div className="pipeline">
          {STATUS_ORDER.map((s) => {
            const count = tasks.filter((t) => t.status === s).length;
            return (
              <div
                key={s}
                className={'pipeline-seg' + (filters.status === s ? ' active' : '')}
                style={{ '--seg': STATUS_COLORS[s] }}
                title={`Show only ${s}`}
                onClick={() => setFilter('status', filters.status === s ? '' : s)}
              >
                <div className="n" style={{ color: count ? STATUS_COLORS[s] : 'var(--text-dim)' }}>{count}</div>
                <div className="l">{STATUS_SHORT[s]}</div>
              </div>
            );
          })}
        </div>
      )}

      {loading && <div className="empty">Loading…</div>}

      {!loading && tasks.length === 0 && filters.view !== 'calendar' && (
        <EmptyState
          title="Nothing matches"
          hint="A filter may be hiding the work. Clear them to see everything."
          action={
            <div className="row" style={{ justifyContent: 'center' }}>
              <button className="btn" onClick={clearFilters}>Clear filters</button>
              {me.role === 'admin' && (
                <button className="btn primary" onClick={() => setCreating(true)}>New task</button>
              )}
            </div>
          }
        />
      )}

      {!loading && tasks.length > 0 && filters.view === 'board' && (
        <div className="board">
          {columns.map((col) => (
            <div
              key={col.status}
              className={'column' + (overStatus === col.status ? ' drag-over' : '')}
              style={{ '--seg': STATUS_COLORS[col.status] }}
              onDragOver={(e) => { e.preventDefault(); setOverStatus(col.status); }}
              onDragLeave={() => setOverStatus((s) => (s === col.status ? null : s))}
              onDrop={(e) => {
                e.preventDefault();
                setOverStatus(null);
                const task = tasks.find((t) => t.id === e.dataTransfer.getData('text/plain'));
                if (task) move(task, col.status);
                setDragId(null);
              }}
            >
              <div className="column-head">
                <span className="dot" style={{ background: STATUS_COLORS[col.status] }} />
                {STATUS_SHORT[col.status]}
                <span className="count">{col.items.length}</span>
              </div>
              {col.items.map((t) => (
                <TaskCard
                  key={t.id}
                  task={t}
                  client={clientOf(t)}
                  assignees={peopleOf(t)}
                  dragging={dragId === t.id}
                  onDragStart={onDragStart}
                  onOpen={(id) => go('task', id)}
                />
              ))}
            </div>
          ))}
        </div>
      )}

      {!loading && filters.view === 'calendar' && (
        <CalendarView
          month={calMonth}
          tasks={listed}
          directory={directory}
          go={go}
          onMonth={setCalMonth}
          onMove={(taskId, day) => {
            const previous = tasks;
            setTasks((list) => list.map((t) => (t.id === taskId ? { ...t, due_date: day } : t)));
            api.patch(`/api/tasks/${taskId}`, { due_date: day })
              .catch((e) => { toast(e.message, 'error'); setTasks(previous); });
          }}
        />
      )}

      {!loading && tasks.length > 0 && filters.view === 'list' && (
        <div className="card" style={{ overflowX: 'auto' }}>
          <table className="list">
            <thead>
              <tr>
                <th>Ad</th><th>Client</th><th>Angle</th><th>Format</th>
                <th>Assigned</th><th>Deadline</th><th>Status</th><th>Delivery</th><th>Created</th>
              </tr>
            </thead>
            <tbody>
              {listed.map((t) => {
                const due = dueLabel(t.due_date);
                return (
                  <tr key={t.id} onClick={() => go('task', t.id)}>
                    <td style={{ fontWeight: 600 }}>
                      <div className="row" style={{ gap: 7 }}>
                        <Icon name={t.type === 'video' ? 'video' : 'image'} size={14} />
                        {t.title}
                      </div>
                    </td>
                    <td>{(clientOf(t) || {}).name || '—'}</td>
                    <td className="muted">{t.angle || '—'}</td>
                    <td className="muted">{t.format || '—'}</td>
                    <td><AvatarStack users={peopleOf(t)} /></td>
                    <td>{due ? <span className={'chip ' + due.tone}>{due.text}</span> : <span className="muted">—</span>}</td>
                    <td><StatusPill status={t.status} /></td>
                    <td>
                      {t.delivery_url
                        ? <a href={t.delivery_url} target="_blank" rel="noreferrer" onClick={(e) => e.stopPropagation()}>open</a>
                        : <span className="muted">—</span>}
                    </td>
                    <td className="small muted">{ago(t.created_at)}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}

      {creating && (
        <NewTaskModal
          clients={directory.clients}
          users={directory.users}
          onClose={() => setCreating(false)}
          onCreated={() => load()}
        />
      )}
    </>
  );
}
