/* global React, Icon, Avatar, PageHeader, apiGet, apiPost, upsertProject,
   ACCENTS, accentVars, groupRows, newId, initialsOf, ReadBlock, EditBlock, InsertDivider */

// The customizable block system (section/checklist blocks, markdown, links, layout,
// accent palette) lives in page-blocks.jsx and is shared with the Dashboard. This file
// holds only the project-specific hub: the team roster panel, the linked Drive browser,
// and the ProjectHub page that wires them to the block editor. See
// tasks/project-resource-hub.md and tasks/dashboard-customizable-tiles.md.

const { useState, useEffect, useMemo, useRef } = React;

const normalizeName = (name) => String(name || '').trim().toLowerCase().replace(/\s+/g, ' ');

// === Team roster (always-on, top-right) =====================================
// Derived from the project's memberships — not editable here. Members are managed in
// the Projects tab; this panel always reflects the current team.
function TeamMember({ member }) {
  const [copied, setCopied] = useState(false);
  async function copyEmail() {
    try {
      await navigator.clipboard.writeText(member.email);
      setCopied(true);
      setTimeout(() => setCopied(false), 1400);
    } catch (e) { /* clipboard blocked — mailto link still works */ }
  }
  return (
    <li className="team-member">
      <Avatar initials={member.initials || initialsOf(member.name)} size={34}/>
      <span className="team-member-main">
        <span className="team-member-name">{member.name || 'Unnamed member'}</span>
        {member.email
          ? <a className="team-member-email" href={`mailto:${member.email}`}>{member.email}</a>
          : <span className="team-member-email is-missing">No email on file</span>}
      </span>
      {member.email && (
        <button type="button" className="team-member-copy" onClick={copyEmail}
          aria-label={copied ? 'Email copied' : 'Copy email'}
          title={copied ? 'Copied!' : 'Copy email'}>
          <Icon name={copied ? 'check' : 'clipboard'} size={14}/>
        </button>
      )}
    </li>
  );
}

function TeamPanel({ members }) {
  return (
    <aside className="team-panel" aria-label="Project team">
      <div className="team-panel-head">
        <span className="team-panel-title"><Icon name="users" size={14}/> Team</span>
        <span className="team-panel-count">{members.length}</span>
      </div>
      {members.length === 0 ? (
        <div className="team-empty">No members assigned to this project yet.</div>
      ) : (
        <ul className="team-list">
          {members.map((m) => <TeamMember key={m.id} member={m}/>)}
        </ul>
      )}
    </aside>
  );
}

// === Team Drive (read-only folder browse) ==================================
// Surfaces the one Google Drive folder linked to this project (admins link it in the
// Projects tab). Structure + metadata only, with links that open each item in Google.
// Server-side access is gated by project membership. See tasks/per-team-drive-folders.md.
function DriveKindIcon({ kind }) {
  const map = {
    pdf:      { name: 'pdf',    color: '#9f3d35', bg: '#f8d8d2' },
    sheet:    { name: 'sheet',  color: '#3d7c5a', bg: '#d9efdf' },
    slides:   { name: 'slides', color: '#9a6a22', bg: '#f6e6bf' },
    folder:   { name: 'folder', color: '#496f86', bg: '#dbe8ef' },
    doc:      { name: 'doc',    color: '#1a73e8', bg: '#e8f0fe' },
    form:     { name: 'check-square', color: '#7c3aed', bg: '#ede9fe' },
    drawing:  { name: 'filter', color: '#c2410c', bg: '#ffedd5' },
    shortcut: { name: 'arrow-up-right', color: '#64748b', bg: '#f1f5f9' },
    image:    { name: 'layers', color: '#0d9488', bg: '#ccfbf1' },
    video:    { name: 'play',   color: '#b45309', bg: '#fef3c7' },
    file:     { name: 'paperclip', color: '#57534e', bg: '#f5f5f4' },
  };
  const m = map[kind] || map.file;
  return (
    <span style={{
      display: 'inline-grid', placeItems: 'center', flexShrink: 0,
      width: 26, height: 26, borderRadius: 4, background: m.bg, color: m.color,
    }}>
      <Icon name={m.name} size={13} stroke={1.4}/>
    </span>
  );
}

function TeamDriveRow({ item, depth, expanded, onToggle }) {
  const isFolder = item.kind === 'folder';
  const isOpen = expanded.has(item.id);
  const children = Array.isArray(item.children) ? item.children : [];
  return (
    <>
      <div className="row" style={{ gap: 8, padding: '5px 6px', paddingLeft: 6 + depth * 20, minWidth: 0 }}>
        {isFolder ? (
          <button type="button" className="btn-icon" style={{ width: 22, height: 22, border: 0 }}
            aria-label={isOpen ? 'Collapse' : 'Expand'} aria-expanded={isOpen}
            onClick={() => onToggle(item.id)}>
            <Icon name={isOpen ? 'chevron-down' : 'chevron-right'} size={13}/>
          </button>
        ) : <span style={{ width: 22, height: 22, display: 'inline-block' }}/>}
        <DriveKindIcon kind={item.kind}/>
        <a href={item.url} target="_blank" rel="noopener noreferrer"
          style={{ color: 'var(--steel-900)', fontWeight: 500, fontSize: 13.5, textDecoration: 'none',
            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', flex: 1, minWidth: 0 }}>
          {item.name}
        </a>
        {isFolder
          ? <span className="muted" style={{ fontSize: 12 }}>{children.length} item{children.length === 1 ? '' : 's'}</span>
          : item.modified && <span className="mono muted" style={{ fontSize: 11.5 }}>{item.modified}</span>}
        <a className="btn-icon" href={item.url} target="_blank" rel="noopener noreferrer"
          title="Open in Google Drive" style={{ width: 24, height: 24 }}>
          <Icon name="external" size={12}/>
        </a>
      </div>
      {isFolder && isOpen && children.map((child) => (
        <TeamDriveRow key={child.id} item={child} depth={depth + 1} expanded={expanded} onToggle={onToggle}/>
      ))}
      {isFolder && isOpen && children.length === 0 && (
        <div className="muted" style={{ fontSize: 12.5, padding: '4px 0', paddingLeft: 28 + (depth + 1) * 20 }}>
          Empty folder.
        </div>
      )}
    </>
  );
}

function TeamDrive({ projectId, isAdmin }) {
  const [folder, setFolder] = useState(null);
  const [status, setStatus] = useState('loading'); // loading | ready | empty | error
  const [error, setError] = useState('');
  const [expanded, setExpanded] = useState(() => new Set());

  useEffect(() => {
    if (!projectId) return undefined;
    let cancelled = false;
    setStatus('loading'); setError(''); setFolder(null); setExpanded(new Set());
    apiGet(`/api/projects/${encodeURIComponent(projectId)}/drive/files`)
      .then((data) => { if (!cancelled) { setFolder(data.folder || null); setStatus('ready'); } })
      .catch((err) => {
        if (cancelled) return;
        if (err.status === 424) setStatus('empty');
        else { setError(err.message || 'Could not load the team Drive folder.'); setStatus('error'); }
      });
    return () => { cancelled = true; };
  }, [projectId]);

  const toggle = (id) => setExpanded((cur) => {
    const next = new Set(cur);
    if (next.has(id)) next.delete(id); else next.add(id);
    return next;
  });

  // No folder linked: nudge admins toward the Projects tab; show nothing to members.
  if (status === 'empty') {
    if (!isAdmin) return null;
    return (
      <section className="card" style={{ padding: 16, marginTop: 18 }}>
        <div className="field-label" style={{ marginBottom: 6 }}>
          <Icon name="folder" size={14} style={{ verticalAlign: 'middle', marginRight: 5 }}/> Team Drive
        </div>
        <div className="muted" style={{ fontSize: 12.5, lineHeight: 1.5 }}>
          No Drive folder linked yet. Add a folder link in the Projects tab to surface this team's files here.
        </div>
      </section>
    );
  }

  const children = Array.isArray(folder?.children) ? folder.children : [];

  return (
    <section className="card" style={{ padding: 16, marginTop: 18 }}>
      <div className="row between" style={{ marginBottom: 10, alignItems: 'center' }}>
        <span className="field-label" style={{ margin: 0 }}>
          <Icon name="folder" size={14} style={{ verticalAlign: 'middle', marginRight: 5 }}/> Team Drive
        </span>
        {folder?.url && (
          <a className="linklike" href={folder.url} target="_blank" rel="noopener noreferrer" style={{ fontSize: 12.5 }}>
            <Icon name="external" size={12}/> Open in Drive
          </a>
        )}
      </div>
      {status === 'loading' && <div className="muted" style={{ fontSize: 12.5 }}>Loading files…</div>}
      {status === 'error' && <div className="form-error" style={{ fontSize: 12.5 }}>{error}</div>}
      {status === 'ready' && children.length === 0 && (
        <div className="muted" style={{ fontSize: 12.5 }}>This folder is empty, or the service account can't see it.</div>
      )}
      {status === 'ready' && children.length > 0 && (
        <div>
          {children.map((item) => (
            <TeamDriveRow key={item.id} item={item} depth={0} expanded={expanded} onToggle={toggle}/>
          ))}
        </div>
      )}
    </section>
  );
}

// === Main =================================================================
function ProjectHub({ projects = [], setProjects, members = [], currentUser, viewMode = 'member', route = '' }) {
  const isAdminView = Boolean(currentUser?.isAdmin && viewMode === 'admin');

  const myProjectIds = useMemo(() => {
    const email = String(currentUser?.email || '').toLowerCase();
    const name = normalizeName(currentUser?.name);
    const me = members.find((m) => String(m.email || '').toLowerCase() === email)
      || members.find((m) => normalizeName(m.name) === name);
    if (!me) return new Set();
    return new Set(
      projects.filter((p) => (p.memberships || []).some((ms) => ms.memberId === me.id)).map((p) => p.id)
    );
  }, [projects, members, currentUser]);

  // Open boards (server flag `openToClub`) are visible to everyone; membership still
  // decides whether they are editable — see canManage below.
  const projectList = isAdminView
    ? projects
    : projects.filter((p) => myProjectIds.has(p.id) || p.openToClub);
  const routeProjectId = route.startsWith('project:') ? route.slice('project:'.length) : null;
  const [adminPick, setAdminPick] = useState('');
  const selectedId = routeProjectId || adminPick || projectList[0]?.id || '';
  const selected = projects.find((p) => p.id === selectedId) || null;
  const canManage = Boolean(selected) && (currentUser?.isAdmin || myProjectIds.has(selected.id));
  // Renaming the project itself is admin-only (POST /api/projects is requireAdmin),
  // even though any team member can customize the page around it.
  const canRename = Boolean(selected) && Boolean(currentUser?.isAdmin) && typeof setProjects === 'function';

  // The team roster always shown top-right — derived from the project's memberships.
  const team = useMemo(() => {
    if (!selected) return [];
    const ids = new Set((selected.memberships || []).map((ms) => ms.memberId));
    return members
      .filter((m) => ids.has(m.id))
      .sort((a, b) => String(a.name || '').localeCompare(String(b.name || '')));
  }, [selected, members]);

  const [page, setPage] = useState({ accent: 'coral', coverEmoji: '', blocks: [] });
  const [status, setStatus] = useState('idle');
  const [error, setError] = useState('');
  const [editing, setEditing] = useState(false);
  const [saving, setSaving] = useState(false);
  // Cover-title draft, live only while editing; '' outside edit mode.
  const [nameDraft, setNameDraft] = useState('');
  const serverPage = useRef(null);
  const [dragId, setDragId] = useState(null);
  // Read-mode autosave (to-do check-offs). pageRef mirrors `page` synchronously so
  // rapid toggles never persist a stale base.
  const pageRef = useRef(page);
  useEffect(() => { pageRef.current = page; }, [page]);
  const [todoSaveState, setTodoSaveState] = useState('idle'); // idle | saving | error

  useEffect(() => {
    if (!selectedId) { setStatus('idle'); return undefined; }
    let cancelled = false;
    setStatus('loading');
    setError('');
    setEditing(false);
    apiGet(`/api/projects/${encodeURIComponent(selectedId)}/page`)
      .then((data) => {
        if (cancelled) return;
        const loaded = data.page || { accent: 'coral', coverEmoji: '', blocks: [] };
        serverPage.current = loaded;
        setPage(loaded);
        setStatus('ready');
      })
      .catch((err) => {
        if (cancelled) return;
        setError(err.message || 'Could not load this page.');
        setStatus('error');
      });
    return () => { cancelled = true; };
  }, [selectedId]);

  // --- block mutations (edit mode) ---
  const patchBlock = (id, patch) =>
    setPage((p) => ({ ...p, blocks: p.blocks.map((b) => (b.id === id ? { ...b, ...patch } : b)) }));
  const removeBlock = (id) => setPage((p) => ({ ...p, blocks: p.blocks.filter((b) => b.id !== id) }));
  const setWidth = (id, width) => patchBlock(id, { width: width === 'half' ? 'half' : 'full' });
  // atIndex null → append; otherwise insert at that position (insert-between-rows).
  const addBlock = (type = 'section', atIndex = null) => setPage((p) => {
    const base = { id: newId('blk'), width: 'full', title: '' };
    const block = type === 'todos'
      ? { ...base, type: 'todos', groups: [] }
      : { ...base, type: 'section', text: '', items: [] };
    const blocks = [...p.blocks];
    const i = atIndex == null ? blocks.length : Math.max(0, Math.min(atIndex, blocks.length));
    blocks.splice(i, 0, block);
    return { ...p, blocks };
  });

  // Read-mode: flip a to-do's `done` and autosave the whole page. Gated on canManage —
  // a viewer of an open board can read it but must not tick someone else's to-dos, and
  // POSTing anyway would just 403. Persists the exact computed next page (via pageRef)
  // so quick successive ticks don't race.
  const persistPage = (nextPage) => {
    if (!selectedId || !canManage) return;
    setTodoSaveState('saving');
    apiPost(`/api/projects/${encodeURIComponent(selectedId)}/page`, nextPage)
      .then((data) => { serverPage.current = data.page || nextPage; setTodoSaveState('idle'); })
      .catch(() => setTodoSaveState('error'));
  };
  const toggleTask = (blockId, groupId, taskId) => {
    if (!canManage) return;
    const next = {
      ...pageRef.current,
      blocks: pageRef.current.blocks.map((b) => {
        if (b.id !== blockId || b.type !== 'todos') return b;
        return {
          ...b,
          groups: (b.groups || []).map((g) => (g.id !== groupId ? g : {
            ...g,
            tasks: (g.tasks || []).map((t) => (t.id !== taskId ? t : { ...t, done: !t.done })),
          })),
        };
      }),
    };
    pageRef.current = next;
    setPage(next);
    persistPage(next);
  };
  const moveBlock = (fromId, toId) => {
    if (!fromId || fromId === toId) return;
    setPage((p) => {
      const from = p.blocks.findIndex((b) => b.id === fromId);
      const to = p.blocks.findIndex((b) => b.id === toId);
      if (from < 0 || to < 0) return p;
      const next = [...p.blocks];
      const [moved] = next.splice(from, 1);
      next.splice(to, 0, moved);
      return { ...p, blocks: next };
    });
  };
  const dragHandlers = {
    active: Boolean(dragId),
    onStart: setDragId,
    onEnd: () => setDragId(null),
    onDrop: (toId) => { moveBlock(dragId, toId); setDragId(null); },
  };

  const savedName = selected ? (selected.title || selected.name || '') : '';

  function startEdit() {
    setError('');
    setNameDraft(savedName);
    // Pin the selection: `projectList` is name-sorted, so a rename can change which
    // project is projectList[0] — the implicit pick when the admin never touched the
    // picker — and would otherwise swap the hub out mid-edit.
    if (selectedId) setAdminPick(selectedId);
    setEditing(true);
  }
  function cancelEdit() {
    if (serverPage.current) setPage(serverPage.current);
    setError('');
    setNameDraft('');
    setEditing(false);
    setDragId(null);
  }
  // Persist a renamed project through the same admin endpoint the Projects tab
  // uses. Sends the existing memberships (the handler replaces the roster on every
  // save) and omits driveFolderUrl (omitting it leaves a linked folder alone).
  function saveName(nextTitle) {
    return apiPost('/api/projects', {
      id: selected.id,
      title: nextTitle,
      memberships: selected.memberships || [],
    }).then((result) => {
      setProjects(result.projects || upsertProject(projects, result.project));
    });
  }

  function save() {
    const nextName = nameDraft.trim();
    if (canRename && !nextName) {
      setError('Give the project a name before saving.');
      return;
    }
    setSaving(true);
    setError('');
    // Rename first: it can fail on the projects.name UNIQUE constraint, and failing
    // before the page write leaves the block edits in the working copy to retry.
    const rename = canRename && nextName !== savedName ? saveName(nextName) : Promise.resolve();
    // A link row the user actually filled in (non-empty URL) should persist; the
    // server drops anything it can't store. Count attempted-vs-saved so we can flag
    // links that silently didn't save instead of just dropping them.
    const attempted = page.blocks.reduce(
      (n, b) => n + (b.items || []).filter((it) => (it.url || '').trim()).length, 0);
    rename
      .then(() => apiPost(`/api/projects/${encodeURIComponent(selectedId)}/page`, page))
      .then((data) => {
        const saved = data.page || page;
        const persisted = saved.blocks.reduce(
          (n, b) => n + (b.items || []).length, 0);
        serverPage.current = saved;
        if (persisted < attempted) {
          // Keep the working copy (with the bad rows) so the user can fix them.
          const dropped = attempted - persisted;
          setError(`${dropped} link${dropped === 1 ? "" : "s"} couldn’t be saved — check that ${dropped === 1 ? "it has" : "each has"} a valid web address. Everything else was saved.`);
        } else {
          setPage(saved);
          setNameDraft('');
          setEditing(false);
        }
      })
      .catch((err) => setError(err.message || 'Could not save the page.'))
      .finally(() => setSaving(false));
  }

  const projectName = savedName || 'Project';

  if (projectList.length === 0) {
    return (
      <>
        <PageHeader eyebrow="Project · Hub" title="Project Hub"
          sub="A customizable home page for each project team."/>
        <div className="card" style={{ padding: 22 }}>
          <div style={{ fontWeight: 600, color: 'var(--steel-900)', marginBottom: 4 }}>
            {isAdminView ? 'No projects yet' : 'You haven’t been assigned to a project'}
          </div>
          <div className="muted" style={{ fontSize: 13, lineHeight: 1.55 }}>
            {isAdminView
              ? 'Create a project in the Projects tab and assign members — each team then gets its own customizable page here.'
              : 'Once an admin adds you to a project team, it’ll show up as its own tab with the team’s page.'}
          </div>
        </div>
      </>
    );
  }

  const accent = ACCENTS[page.accent] ? page.accent : 'coral';

  return (
    <div className="proj-page" style={accentVars(accent)}>
      {/* Cover */}
      <div className="proj-cover">
        <div className="proj-cover-main">
          <span className="proj-cover-emoji">
            {page.coverEmoji || ((editing && canRename ? nameDraft : projectName) || 'P').trim().slice(0, 1).toUpperCase() || 'P'}
          </span>
          <div style={{ minWidth: 0 }}>
            <div className="proj-cover-eyebrow">{editing && canRename ? 'Project name' : 'Project Hub'}</div>
            {editing && canRename ? (
              <input className="proj-cover-title-input" value={nameDraft} maxLength={120}
                aria-label="Project name" placeholder="Project name" autoComplete="off"
                onChange={(e) => setNameDraft(e.target.value)}
                onKeyDown={(e) => { if (e.key === 'Enter' && !saving) save(); }}/>
            ) : (
              <h1 className="proj-cover-title">{projectName}</h1>
            )}
          </div>
        </div>
        <div className="proj-cover-actions">
          {isAdminView && !editing && (
            <label className="hub-project-picker">
              <span className="micro-label">Project</span>
              <select className="select" value={selectedId} onChange={(e) => setAdminPick(e.target.value)}>
                {projectList.map((p) => <option key={p.id} value={p.id}>{p.title || p.name}</option>)}
              </select>
            </label>
          )}
          {canManage && status === 'ready' && !editing && (
            <button type="button" className="btn btn-on-accent" onClick={startEdit}>
              <Icon name="edit" size={14}/> Customize
            </button>
          )}
        </div>
      </div>

      {error && <div className="form-error" style={{ margin: '16px 0' }}>{error}</div>}

      {/* Edit toolbar */}
      {editing && (
        <div className="proj-edit-bar">
          <span className="proj-edit-badge"><Icon name="edit" size={13}/> Editing</span>
          <div className="proj-edit-group">
            <span className="micro-label">Accent</span>
            <div className="accent-swatches">
              {Object.keys(ACCENTS).map((key) => (
                <button key={key} type="button"
                  className={`accent-swatch ${page.accent === key ? 'active' : ''}`}
                  title={ACCENTS[key].label} style={{ background: ACCENTS[key].solid }}
                  onClick={() => setPage((p) => ({ ...p, accent: key }))}/>
              ))}
            </div>
          </div>
          <label className="proj-edit-group">
            <span className="micro-label">Cover icon</span>
            <input className="input proj-emoji-input" value={page.coverEmoji} maxLength={8}
              placeholder="🚀" onChange={(e) => setPage((p) => ({ ...p, coverEmoji: e.target.value }))}/>
          </label>
          <div className="proj-edit-spacer"/>
          <button type="button" className="btn btn-ghost" onClick={cancelEdit} disabled={saving}>Cancel</button>
          <button type="button" className="btn btn-primary" onClick={save} disabled={saving}>
            <Icon name="check" size={14}/> {saving ? 'Saving…' : 'Save page'}
          </button>
        </div>
      )}

      {/* Body: the team roster floats top-right at a set size; content flows beside it
          and reclaims full width once it extends past the panel's bottom. The panel is
          rendered first so the following content wraps around the float. */}
      <div className="proj-body">
        <TeamPanel members={team}/>
        <div className="proj-main">
          {!editing && todoSaveState !== 'idle' && (
            <div className={`todo-save-note${todoSaveState === 'error' ? ' is-error' : ''}`}>
              {todoSaveState === 'saving'
                ? <span className="todo-save-dot" aria-hidden="true"/>
                : <Icon name="x" size={12}/>}
              {todoSaveState === 'saving' ? ' Saving…' : ' Couldn’t save — check your connection.'}
            </div>
          )}
          {status === 'loading' && <div className="card muted" style={{ padding: 22, fontSize: 13 }}>Loading…</div>}

          {status === 'ready' && page.blocks.length === 0 && !editing && (
            <div className="card" style={{ padding: 26, textAlign: 'center' }}>
              <div style={{ fontWeight: 600, color: 'var(--steel-900)', marginBottom: 4 }}>This page is a blank canvas</div>
              <div className="muted" style={{ fontSize: 13, lineHeight: 1.55, marginBottom: canManage ? 16 : 0 }}>
                {canManage
                  ? 'Add sections with notes and links, arrange them however your team likes, and pick an accent.'
                  : 'Your team hasn’t set up this page yet.'}
              </div>
              {canManage && (
                <button type="button" className="btn btn-primary btn-sm" onClick={startEdit}>
                  <Icon name="edit" size={14}/> Customize this page
                </button>
              )}
            </div>
          )}

          {/* Blocks — grouped into rows so half sections pair up and each row wraps the
              floated team panel, then spans full width below it. */}
          {status === 'ready' && (page.blocks.length > 0 || editing) && (
            <div className="proj-blocks">
              {(() => {
                const nodes = [];
                let idx = 0;
                groupRows(page.blocks).forEach((row) => {
                  // In edit mode, a positional insert point precedes each row.
                  if (editing) {
                    nodes.push(<InsertDivider key={`ins-${idx}`} index={idx} onInsert={addBlock}/>);
                  }
                  nodes.push(
                    <div key={row.blocks.map((b) => b.id).join('+')} className={`pb-row pb-row-${row.kind}`}>
                      {row.blocks.map((block) => (
                        editing
                          ? <EditBlock key={block.id} block={block}
                              onPatch={(patch) => patchBlock(block.id, patch)}
                              onRemove={() => removeBlock(block.id)}
                              onWidth={(w) => setWidth(block.id, w)}
                              dragHandlers={dragHandlers}
                              isDragging={dragId === block.id}
                              isDrop={Boolean(dragId) && dragId !== block.id}
                              team={team}/>
                          : <ReadBlock key={block.id} block={block} onToggleTask={toggleTask}
                              readOnly={!canManage}/>
                      ))}
                    </div>
                  );
                  idx += row.blocks.length;
                });
                return nodes;
              })()}
            </div>
          )}

          {/* Team Drive — read-only browse of the project's linked Drive folder. Hidden
              while editing the page (folder linking happens in the Projects tab), and
              hidden from viewers of an open board: the folder stays team-only, and the
              endpoint would 403 for them anyway. */}
          {!editing && selectedId && canManage && (
            <TeamDrive projectId={selectedId} isAdmin={Boolean(currentUser?.isAdmin)}/>
          )}

          {/* Add-block picker (edit mode) — descriptive type cards. */}
          {editing && (
            <div className="proj-add">
              <div className="proj-add-label"><Icon name="plus" size={13}/> Add a block</div>
              <div className="proj-add-types">
                <button type="button" className="add-type" onClick={() => addBlock('section')}>
                  <span className="add-type-icon"><Icon name="layers" size={18}/></span>
                  <span className="add-type-body">
                    <span className="add-type-name">Section</span>
                    <span className="add-type-desc">Notes, rich text & links</span>
                  </span>
                </button>
                <button type="button" className="add-type" onClick={() => addBlock('todos')}>
                  <span className="add-type-icon"><Icon name="check-square" size={18}/></span>
                  <span className="add-type-body">
                    <span className="add-type-name">Checklist</span>
                    <span className="add-type-desc">Per-person to-do list</span>
                  </span>
                </button>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

window.ProjectHub = ProjectHub;
