Let members view a workspace's dashboard, editing still for managers only

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-19 15:48:23 +01:00
co-authored by Claude Opus 5.5
parent 391943a1c2
commit dd1e9e0cfe
7 changed files with 25 additions and 39 deletions
@@ -17,6 +17,10 @@ type Props = {
* What can be done with it, after the open button.
*/
children?: ReactNode;
/**
* Link the title to its settings page. Off for people who can't open it.
*/
linked?: boolean;
};
/**
@@ -24,14 +28,14 @@ type Props = {
* settings page from the title. Every list uses it, so the row behaves the
* same, and the open button shows it in the viewer over the page.
*/
export default function ComputerListRow({ computer, badge, subtitle, children }: Props) {
export default function ComputerListRow({ computer, badge, subtitle, children, linked = true }: Props) {
const name = computer.name || `Computer ${computer.id}`;
const ready = computer.status == "running";
return (
<SettingsListRow
avatar={<ComputerIconTile />}
href={`/admin/settings/computers/${computer.id}`}
href={linked ? `/admin/settings/computers/${computer.id}` : undefined}
title={name}
badge={badge}
subtitle={
@@ -8,13 +8,17 @@ type Props = {
* The computers linked to the workspace, its default first.
*/
computers: ComputerWithStatus[];
/**
* Whether the viewer can open each computer's settings page.
*/
linked: boolean;
};
/**
* The computers bots work on here, with their state. Display only:
* computers are linked and set up under Settings.
*/
export default function WorkspaceComputersListGroup({ computers }: Props) {
export default function WorkspaceComputersListGroup({ computers, linked }: Props) {
return (
<SettingsGroup
id="computers"
@@ -30,6 +34,7 @@ export default function WorkspaceComputersListGroup({ computers }: Props) {
key={computer.id}
computer={computer}
is_default={index == 0}
linked={linked}
/>
))}
</SettingsList>
@@ -4,12 +4,16 @@ import type { ComputerWithStatus } from "@/src/types";
type Props = {
computer: ComputerWithStatus;
is_default: boolean;
/**
* Whether the viewer can open its settings page.
*/
linked: boolean;
};
/**
* A computer in the workspace dashboard: its state, resources and what
* it's for, opening its settings page.
* it's for, opening its settings page for those who manage the crew.
*/
export default function WorkspaceDashboardComputerRow({ computer, is_default }: Props) {
return <ComputerListRow computer={computer} badge={is_default ? "Default" : undefined} />;
export default function WorkspaceDashboardComputerRow({ computer, is_default, linked }: Props) {
return <ComputerListRow computer={computer} badge={is_default ? "Default" : undefined} linked={linked} />;
}
@@ -48,6 +48,7 @@ export default function WorkspaceDashboard() {
/>
<WorkspaceComputersListGroup
computers={pageProps?.workspace_computers || []}
linked={can_manage}
/>
</SettingsShell>
);
@@ -29,7 +29,7 @@ export const server: BunextPageServerFn<PagePropsType> = async (params) => {
redirect: {
destination: canManageCrew({ user_types })
? `/admin/settings/workspaces/${query.workspace_id}`
: "/admin/bots",
: `/admin/workspaces/${query.workspace_id}`,
},
};
}
@@ -4,18 +4,17 @@ import grabWorkspaceActivity from "@/src/functions/backend/db/workspaces/grab-wo
import grabWorkspaceBots from "@/src/functions/backend/db/workspaces/grab-workspace-bots";
import grabWorkspaceComputers from "@/src/functions/backend/db/workspaces/grab-workspace-computers";
import { AdminHomePath } from "@/src/dict/admin-sections-dict";
import canManageCrew from "@/src/utils/rbac/can-manage-crew";
import type { PagePropsType, PageQueryObject } from "@/src/types";
import type { BunextPageServerFn } from "@moduletrace/bunext/types";
/**
* A workspace's dashboard: its picture, context, latest activity, bots and
* computers, all read-only. Editing lives under Settings. Only super admins
* and admins see it; a member opens its first bot instead.
* computers, all read-only. Everyone who reaches the workspace sees it; editing lives
* under Settings, for super admins and admins only.
*/
export const server: BunextPageServerFn<PagePropsType> = async (params) => {
const query: PageQueryObject = params.query;
const { user, user_types } = await userAuth({ req: params.req });
const { user } = await userAuth({ req: params.req });
if (!user?.id) {
return { redirect: { destination: "/auth/login" } };
@@ -32,33 +31,6 @@ export const server: BunextPageServerFn<PagePropsType> = async (params) => {
const workspace_id = Number(workspace.id);
/**
* Members don't manage it: they go straight to its first bot.
*/
if (!canManageCrew({ user_types })) {
const workspace_bots = await grabWorkspaceBots({
user_id: user.id,
workspace_id,
});
if (workspace_bots[0]?.id) {
return {
redirect: {
destination: `/admin/workspaces/${workspace_id}/bots/${workspace_bots[0].id}`,
},
};
}
return {
props: {
workspaces,
workspace,
bots: workspace_bots,
workspace_bots,
},
};
}
const [workspace_bots, workspace_computers, workspace_activity] =
await Promise.all([
grabWorkspaceBots({ user_id: user.id, workspace_id }),