// §22.10 (M3) — the deployment directory at `/`. Renders the caller-visible // projects (from /api/deployment) as cards linking into each project's // `/p//` home. App's DeploymentLanding only mounts this when the N=1 // land-in-corpus redirect does not apply (2+ visible projects, or no readable // default) so OHM's "land in the corpus" UX is preserved. // // §22 S5 — role-aware empty states + the global-Owner create-project action. // The deployment payload carries a `viewer` block; the directory reads it to // render: // * C3.1: a global Owner sees a "Create your first project" CTA (and a "New // project" control when the directory is non-empty), opening the // create-project modal (choose id, name, type, visibility). // * C3.2: a non-owner (a granted account with no roles) sees the empty // directory with no create action and a note that nothing is shared yet. import { useState } from 'react' import { Link } from 'react-router-dom' import { useDeployment } from '../context/DeploymentProvider' import { entryNoun } from './ProjectLayout.jsx' import CreateProjectModal from './CreateProjectModal.jsx' export default function Directory() { const { name, tagline, projects, viewer, refresh } = useDeployment() const [createOpen, setCreateOpen] = useState(false) const canCreate = !!viewer?.can_create_project return (

{name}

{canCreate && projects.length > 0 && ( )}
{tagline &&

{tagline}

} {projects.length === 0 ? ( // C3.1 / C3.2: role-keyed empty state. canCreate ? (

No projects yet.

) : (

Nothing has been shared with you yet.

) ) : (
    {projects.map(p => (
  • {p.name} {entryNoun(p.type)}s
  • ))}
)}
{createOpen && ( setCreateOpen(false)} onCreated={() => { setCreateOpen(false); refresh?.() }} /> )}
) }