mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: add View Source button for template administrators in workspace creation (#18951)
This commit is contained in:
@@ -65,7 +65,10 @@ const CreateWorkspacePage: FC = () => {
|
||||
});
|
||||
const permissionsQuery = useQuery({
|
||||
...checkAuthorization({
|
||||
checks: createWorkspaceChecks(templateQuery.data?.organization_id ?? ""),
|
||||
checks: createWorkspaceChecks(
|
||||
templateQuery.data?.organization_id ?? "",
|
||||
templateQuery.data?.id,
|
||||
),
|
||||
}),
|
||||
enabled: !!templateQuery.data,
|
||||
});
|
||||
@@ -208,6 +211,7 @@ const CreateWorkspacePage: FC = () => {
|
||||
startPollingExternalAuth={startPollingExternalAuth}
|
||||
hasAllRequiredExternalAuth={hasAllRequiredExternalAuth}
|
||||
permissions={permissionsQuery.data as CreateWorkspacePermissions}
|
||||
canUpdateTemplate={permissionsQuery.data?.canUpdateTemplate}
|
||||
parameters={realizedParameters as TemplateVersionParameter[]}
|
||||
presets={templateVersionPresetsQuery.data ?? []}
|
||||
creatingWorkspace={createWorkspaceMutation.isPending}
|
||||
|
||||
@@ -79,7 +79,10 @@ const CreateWorkspacePageExperimental: FC = () => {
|
||||
});
|
||||
const permissionsQuery = useQuery({
|
||||
...checkAuthorization({
|
||||
checks: createWorkspaceChecks(templateQuery.data?.organization_id ?? ""),
|
||||
checks: createWorkspaceChecks(
|
||||
templateQuery.data?.organization_id ?? "",
|
||||
templateQuery.data?.id,
|
||||
),
|
||||
}),
|
||||
enabled: !!templateQuery.data,
|
||||
});
|
||||
@@ -292,6 +295,7 @@ const CreateWorkspacePageExperimental: FC = () => {
|
||||
owner={owner}
|
||||
setOwner={setOwner}
|
||||
autofillParameters={autofillParameters}
|
||||
canUpdateTemplate={permissionsQuery.data?.canUpdateTemplate}
|
||||
error={
|
||||
wsError ||
|
||||
createWorkspaceMutation.error ||
|
||||
|
||||
@@ -28,6 +28,7 @@ const meta: Meta<typeof CreateWorkspacePageView> = {
|
||||
mode: "form",
|
||||
permissions: {
|
||||
createWorkspaceForAny: true,
|
||||
canUpdateTemplate: false,
|
||||
},
|
||||
onCancel: action("onCancel"),
|
||||
},
|
||||
@@ -382,3 +383,23 @@ export const ExternalAuthAllConnected: Story = {
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const WithViewSourceButton: Story = {
|
||||
args: {
|
||||
canUpdateTemplate: true,
|
||||
versionId: "template-version-123",
|
||||
template: {
|
||||
...MockTemplate,
|
||||
organization_name: "default",
|
||||
name: "docker-template",
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"This story shows the View Source button that appears for template administrators. The button allows quick navigation to the template editor from the workspace creation page.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -27,8 +27,10 @@ import { Switch } from "components/Switch/Switch";
|
||||
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete";
|
||||
import { type FormikContextType, useFormik } from "formik";
|
||||
import type { ExternalAuthPollingState } from "hooks/useExternalAuth";
|
||||
import { ExternalLinkIcon } from "lucide-react";
|
||||
import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName";
|
||||
import { type FC, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
getFormHelpers,
|
||||
nameValidator,
|
||||
@@ -67,6 +69,7 @@ interface CreateWorkspacePageViewProps {
|
||||
presets: TypesGen.Preset[];
|
||||
permissions: CreateWorkspacePermissions;
|
||||
creatingWorkspace: boolean;
|
||||
canUpdateTemplate?: boolean;
|
||||
onCancel: () => void;
|
||||
onSubmit: (
|
||||
req: TypesGen.CreateWorkspaceRequest,
|
||||
@@ -92,6 +95,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
|
||||
presets = [],
|
||||
permissions,
|
||||
creatingWorkspace,
|
||||
canUpdateTemplate,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}) => {
|
||||
@@ -218,9 +222,21 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
|
||||
<Margins size="medium">
|
||||
<PageHeader
|
||||
actions={
|
||||
<Button size="sm" variant="outline" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Stack direction="row" spacing={2}>
|
||||
{canUpdateTemplate && (
|
||||
<Button asChild size="sm" variant="outline">
|
||||
<Link
|
||||
to={`/templates/${template.organization_name}/${template.name}/versions/${versionId}/edit`}
|
||||
>
|
||||
<ExternalLinkIcon />
|
||||
View source
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
<Button size="sm" variant="outline" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
<Stack direction="row">
|
||||
|
||||
@@ -20,6 +20,7 @@ const meta: Meta<typeof CreateWorkspacePageViewExperimental> = {
|
||||
parameters: [],
|
||||
permissions: {
|
||||
createWorkspaceForAny: true,
|
||||
canUpdateTemplate: false,
|
||||
},
|
||||
presets: [],
|
||||
sendMessage: () => {},
|
||||
@@ -38,3 +39,23 @@ export const WebsocketError: Story = {
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const WithViewSourceButton: Story = {
|
||||
args: {
|
||||
canUpdateTemplate: true,
|
||||
versionId: "template-version-123",
|
||||
template: {
|
||||
...MockTemplate,
|
||||
organization_name: "default",
|
||||
name: "docker-template",
|
||||
},
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"This story shows the View Source button that appears for template administrators in the experimental workspace creation page. The button allows quick navigation to the template editor.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete";
|
||||
import { type FormikContextType, useFormik } from "formik";
|
||||
import type { ExternalAuthPollingState } from "hooks/useExternalAuth";
|
||||
import { ArrowLeft, CircleHelp } from "lucide-react";
|
||||
import { ArrowLeft, CircleHelp, ExternalLinkIcon } from "lucide-react";
|
||||
import { useSyncFormParameters } from "modules/hooks/useSyncFormParameters";
|
||||
import {
|
||||
Diagnostics,
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import { docs } from "utils/docs";
|
||||
import { nameValidator } from "utils/formUtils";
|
||||
import type { AutofillBuildParameter } from "utils/richParameters";
|
||||
@@ -53,6 +54,7 @@ import type { CreateWorkspacePermissions } from "./permissions";
|
||||
|
||||
interface CreateWorkspacePageViewExperimentalProps {
|
||||
autofillParameters: AutofillBuildParameter[];
|
||||
canUpdateTemplate?: boolean;
|
||||
creatingWorkspace: boolean;
|
||||
defaultName?: string | null;
|
||||
defaultOwner: TypesGen.User;
|
||||
@@ -84,6 +86,7 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
CreateWorkspacePageViewExperimentalProps
|
||||
> = ({
|
||||
autofillParameters,
|
||||
canUpdateTemplate,
|
||||
creatingWorkspace,
|
||||
defaultName,
|
||||
defaultOwner,
|
||||
@@ -378,6 +381,16 @@ export const CreateWorkspacePageViewExperimental: FC<
|
||||
</Badge>
|
||||
)}
|
||||
</span>
|
||||
{canUpdateTemplate && (
|
||||
<Button asChild size="sm" variant="outline">
|
||||
<RouterLink
|
||||
to={`/templates/${template.organization_name}/${template.name}/versions/${versionId}/edit`}
|
||||
>
|
||||
<ExternalLinkIcon />
|
||||
View source
|
||||
</RouterLink>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<span className="flex flex-row items-center gap-2">
|
||||
<h1 className="text-3xl font-semibold m-0">New workspace</h1>
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
export const createWorkspaceChecks = (organizationId: string) =>
|
||||
export const createWorkspaceChecks = (
|
||||
organizationId: string,
|
||||
templateId?: string,
|
||||
) =>
|
||||
({
|
||||
createWorkspaceForAny: {
|
||||
object: {
|
||||
resource_type: "workspace",
|
||||
resource_type: "workspace" as const,
|
||||
organization_id: organizationId,
|
||||
owner_id: "*",
|
||||
},
|
||||
action: "create",
|
||||
action: "create" as const,
|
||||
},
|
||||
...(templateId && {
|
||||
canUpdateTemplate: {
|
||||
object: {
|
||||
resource_type: "template" as const,
|
||||
resource_id: templateId,
|
||||
},
|
||||
action: "update" as const,
|
||||
},
|
||||
}),
|
||||
}) as const;
|
||||
|
||||
export type CreateWorkspacePermissions = Record<
|
||||
|
||||
Reference in New Issue
Block a user