This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// A workspace: its projects, and a form to create another (spec §3.1). The projects are listed
|
||||
// through `tenancy.project.list`, which reads them inside the workspace's own scope -- a workspace of
|
||||
// another organization, or one the person has no place in, is the not-found page.
|
||||
|
||||
import { projectList, workspaceList } from "@conusai-kit/tenancy"
|
||||
import type { ReactElement } from "react"
|
||||
import { orFallback } from "../../../src/server/guard"
|
||||
import { holdsAtLeast, pageContext } from "../../../src/server/page"
|
||||
import { Breadcrumb } from "../../../src/ui/breadcrumb"
|
||||
import { ProcedureForm } from "../../../src/ui/procedure-form"
|
||||
import { PROJECT_MODULE_KEYS, projectHref } from "../../../src/ui/projects"
|
||||
|
||||
export const dynamic = "force-dynamic"
|
||||
|
||||
export default async function WorkspaceHome({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ org: string; workspace: string }>
|
||||
}): Promise<ReactElement> {
|
||||
const { org, workspace } = await params
|
||||
const page = await pageContext()
|
||||
const { caller, i18n, path } = page
|
||||
const { name, projects } = await orFallback(path, async () => {
|
||||
const [{ projects }, { workspaces }] = await Promise.all([
|
||||
caller.call(projectList, { org, workspace }),
|
||||
caller.call(workspaceList, { org }),
|
||||
])
|
||||
// `projectList` answers for the workspace's scope; the name is the list's own, so the page does
|
||||
// not need a procedure of its own to say it.
|
||||
return { projects, name: workspaces.find((entry) => entry.slug === workspace)?.name ?? workspace }
|
||||
})
|
||||
const mayCreate = await holdsAtLeast(page, org, "editor")
|
||||
|
||||
return (
|
||||
<>
|
||||
<Breadcrumb
|
||||
label={i18n.message("shell.breadcrumb")}
|
||||
crumbs={[
|
||||
{ text: i18n.message("app.workspaces"), href: `/${org}/workspaces` },
|
||||
{ text: name, href: `/${org}/${workspace}` },
|
||||
]}
|
||||
/>
|
||||
<h1>{i18n.message("workspace.title", { name })}</h1>
|
||||
<h2>{i18n.message("workspace.projects")}</h2>
|
||||
{projects.length === 0 ? (
|
||||
<p>{i18n.message("workspace.noProjects")}</p>
|
||||
) : (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{i18n.message("app.name")}</th>
|
||||
<th scope="col">{i18n.message("workspace.module")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{projects.map((project) => (
|
||||
<tr key={project.slug}>
|
||||
<td>
|
||||
<a href={projectHref(org, workspace, project.slug)}>{project.name}</a>
|
||||
</td>
|
||||
<td>{project.moduleKey}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
|
||||
{mayCreate ? (
|
||||
<>
|
||||
<h2>{i18n.message("workspace.createProject")}</h2>
|
||||
<ProcedureForm
|
||||
procedure="tenancy.project.create"
|
||||
hidden={{ org, workspace }}
|
||||
submitLabel={i18n.message("app.create")}
|
||||
failedTemplate={i18n.message("app.failed")}
|
||||
fields={[
|
||||
{ name: "name", label: i18n.message("app.name"), required: true },
|
||||
{ name: "slug", label: i18n.message("app.slug"), required: true },
|
||||
{
|
||||
name: "moduleKey",
|
||||
label: i18n.message("workspace.module"),
|
||||
required: true,
|
||||
options: PROJECT_MODULE_KEYS.map((key) => ({ value: key, label: key })),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user