Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d1cd784866 | |||
| db94b30b1c |
Generated
+2
-2
@@ -4845,8 +4845,8 @@ const docTemplate = `{
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+2
-2
@@ -4273,8 +4273,8 @@
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
"200": {
|
||||
"description": "OK"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -513,7 +513,7 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Users
|
||||
// @Param user path string true "User ID, name, or me"
|
||||
// @Success 204
|
||||
// @Success 200
|
||||
// @Router /users/{user} [delete]
|
||||
func (api *API) deleteUser(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
@@ -588,7 +588,9 @@ func (api *API) deleteUser(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusNoContent)
|
||||
httpapi.Write(ctx, rw, http.StatusOK, codersdk.Response{
|
||||
Message: "User has been deleted!",
|
||||
})
|
||||
}
|
||||
|
||||
// Returns the parameterized user requested. All validation
|
||||
|
||||
+3
-1
@@ -309,7 +309,9 @@ func (c *Client) DeleteUser(ctx context.Context, id uuid.UUID) error {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusNoContent {
|
||||
// Check for a 200 or a 204 response. 2.14.0 accidentally included a 204 response,
|
||||
// which was a breaking change, and reverted in 2.14.1.
|
||||
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNoContent {
|
||||
return ReadBodyAsError(res)
|
||||
}
|
||||
return nil
|
||||
|
||||
Generated
+3
-3
@@ -426,9 +426,9 @@ curl -X DELETE http://coder-server:8080/api/v2/users/{user} \
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | --------------------------------------------------------------- | ----------- | ------ |
|
||||
| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | |
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | ------ |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
||||
agpl="${CODER_BUILD_AGPL:-0}"
|
||||
output_path=""
|
||||
version=""
|
||||
sign_windows="${CODER_SIGN_WINDOWS:-0}"
|
||||
|
||||
args="$(getopt -o "" -l agpl,output:,version: -- "$@")"
|
||||
eval set -- "$args"
|
||||
@@ -51,6 +52,11 @@ if [[ "$output_path" == "" ]]; then
|
||||
error "--output is a required parameter"
|
||||
fi
|
||||
|
||||
if [[ "$sign_windows" == 1 ]]; then
|
||||
dependencies java
|
||||
requiredenvs JSIGN_PATH EV_KEYSTORE EV_KEY EV_CERTIFICATE_PATH EV_TSA_URL GCLOUD_ACCESS_TOKEN
|
||||
fi
|
||||
|
||||
if [[ "$#" != 1 ]]; then
|
||||
error "Exactly one argument must be provided to this script, $# were supplied"
|
||||
fi
|
||||
@@ -125,3 +131,7 @@ popd
|
||||
cp "$temp_dir/installer.exe" "$output_path"
|
||||
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
if [[ "$sign_windows" == 1 ]]; then
|
||||
execrelative ./sign_windows.sh "$output_path" 1>&2
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { screen, userEvent } from "@storybook/test";
|
||||
import { CreateTemplateButton } from "./CreateTemplateButton";
|
||||
|
||||
const meta: Meta<typeof CreateTemplateButton> = {
|
||||
title: "pages/TemplatesPage/CreateTemplateButton",
|
||||
component: CreateTemplateButton,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CreateTemplateButton>;
|
||||
|
||||
export const Close: Story = {};
|
||||
|
||||
export const Open: Story = {
|
||||
play: async ({ step }) => {
|
||||
const user = userEvent.setup();
|
||||
await step("click on trigger", async () => {
|
||||
await user.click(screen.getByRole("button"));
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
import AddIcon from "@mui/icons-material/AddOutlined";
|
||||
import Inventory2 from "@mui/icons-material/Inventory2";
|
||||
import NoteAddOutlined from "@mui/icons-material/NoteAddOutlined";
|
||||
import UploadOutlined from "@mui/icons-material/UploadOutlined";
|
||||
import Button from "@mui/material/Button";
|
||||
import type { FC } from "react";
|
||||
import {
|
||||
MoreMenu,
|
||||
MoreMenuContent,
|
||||
MoreMenuItem,
|
||||
MoreMenuTrigger,
|
||||
} from "components/MoreMenu/MoreMenu";
|
||||
|
||||
type CreateTemplateButtonProps = {
|
||||
onNavigate: (path: string) => void;
|
||||
};
|
||||
|
||||
export const CreateTemplateButton: FC<CreateTemplateButtonProps> = ({
|
||||
onNavigate,
|
||||
}) => {
|
||||
return (
|
||||
<MoreMenu>
|
||||
<MoreMenuTrigger>
|
||||
<Button startIcon={<AddIcon />} variant="contained">
|
||||
Create Template
|
||||
</Button>
|
||||
</MoreMenuTrigger>
|
||||
<MoreMenuContent>
|
||||
<MoreMenuItem
|
||||
onClick={() => {
|
||||
onNavigate("/templates/new?exampleId=scratch");
|
||||
}}
|
||||
>
|
||||
<NoteAddOutlined />
|
||||
From scratch
|
||||
</MoreMenuItem>
|
||||
<MoreMenuItem
|
||||
onClick={() => {
|
||||
onNavigate("/templates/new");
|
||||
}}
|
||||
>
|
||||
<UploadOutlined />
|
||||
Upload template
|
||||
</MoreMenuItem>
|
||||
<MoreMenuItem
|
||||
onClick={() => {
|
||||
onNavigate("/starter-templates");
|
||||
}}
|
||||
>
|
||||
<Inventory2 />
|
||||
Choose a starter template
|
||||
</MoreMenuItem>
|
||||
</MoreMenuContent>
|
||||
</MoreMenu>
|
||||
);
|
||||
};
|
||||
@@ -17,7 +17,7 @@ test("create template from scratch", async () => {
|
||||
element: <TemplatesPage />,
|
||||
},
|
||||
{
|
||||
path: "/starter-templates",
|
||||
path: "/templates/new",
|
||||
element: <div data-testid="new-template-page" />,
|
||||
},
|
||||
],
|
||||
@@ -34,6 +34,9 @@ test("create template from scratch", async () => {
|
||||
name: "Create Template",
|
||||
});
|
||||
await user.click(createTemplateButton);
|
||||
const fromScratchMenuItem = await screen.findByText("From scratch");
|
||||
await user.click(fromScratchMenuItem);
|
||||
await screen.findByTestId("new-template-page");
|
||||
expect(router.state.location.pathname).toBe("/starter-templates");
|
||||
expect(router.state.location.pathname).toBe("/templates/new");
|
||||
expect(router.state.location.search).toBe("?exampleId=scratch");
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
TableRowSkeleton,
|
||||
} from "components/TableLoader/TableLoader";
|
||||
import { useClickableTableRow } from "hooks/useClickableTableRow";
|
||||
import { useDashboard } from "modules/dashboard/useDashboard";
|
||||
import { linkToTemplate, useLinks } from "modules/navigation";
|
||||
import { createDayString } from "utils/createDayString";
|
||||
import { docs } from "utils/docs";
|
||||
@@ -45,6 +46,7 @@ import {
|
||||
formatTemplateBuildTime,
|
||||
formatTemplateActiveDevelopers,
|
||||
} from "utils/templates";
|
||||
import { CreateTemplateButton } from "./CreateTemplateButton";
|
||||
import { EmptyTemplates } from "./EmptyTemplates";
|
||||
|
||||
export const Language = {
|
||||
@@ -167,38 +169,40 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
|
||||
examples,
|
||||
canCreateTemplates,
|
||||
}) => {
|
||||
const { experiments } = useDashboard();
|
||||
const isLoading = !templates;
|
||||
const isEmpty = templates && templates.length === 0;
|
||||
const navigate = useNavigate();
|
||||
const multiOrgExperimentEnabled = experiments.includes("multi-organization");
|
||||
|
||||
const createTemplateAction = () => {
|
||||
return multiOrgExperimentEnabled ? (
|
||||
<Button
|
||||
startIcon={<AddIcon />}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
navigate("/starter-templates");
|
||||
}}
|
||||
>
|
||||
Create Template
|
||||
</Button>
|
||||
) : (
|
||||
<CreateTemplateButton onNavigate={navigate} />
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Margins>
|
||||
<PageHeader
|
||||
actions={
|
||||
canCreateTemplates && (
|
||||
<Button
|
||||
startIcon={<AddIcon />}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
navigate("/starter-templates");
|
||||
}}
|
||||
>
|
||||
Create Template
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
>
|
||||
<PageHeader actions={canCreateTemplates && createTemplateAction()}>
|
||||
<PageHeaderTitle>
|
||||
<Stack spacing={1} direction="row" alignItems="center">
|
||||
Templates
|
||||
<TemplateHelpTooltip />
|
||||
</Stack>
|
||||
</PageHeaderTitle>
|
||||
{templates && templates.length > 0 && (
|
||||
<PageHeaderSubtitle>
|
||||
Select a template to create a workspace.
|
||||
</PageHeaderSubtitle>
|
||||
)}
|
||||
<PageHeaderSubtitle>
|
||||
Select a template to create a workspace.
|
||||
</PageHeaderSubtitle>
|
||||
</PageHeader>
|
||||
|
||||
{error ? (
|
||||
@@ -212,7 +216,7 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
|
||||
<TableCell width="15%">{Language.usedByLabel}</TableCell>
|
||||
<TableCell width="10%">{Language.buildTimeLabel}</TableCell>
|
||||
<TableCell width="15%">{Language.lastUpdatedLabel}</TableCell>
|
||||
<TableCell width="1%"></TableCell>
|
||||
<TableCell width="1%" />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
|
||||
@@ -472,6 +472,10 @@ export const router = createBrowserRouter(
|
||||
|
||||
{/* Pages that don't have the dashboard layout */}
|
||||
<Route path="/:username/:workspace" element={<WorkspacePage />} />
|
||||
<Route
|
||||
path="/templates/:template/versions/:version/edit"
|
||||
element={<TemplateVersionEditorPage />}
|
||||
/>
|
||||
<Route
|
||||
path="/templates/:organization/:template/versions/:version/edit"
|
||||
element={<TemplateVersionEditorPage />}
|
||||
|
||||
Reference in New Issue
Block a user