Skip to content

fix: show error when creating a new group fails #11560

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions enterprise/coderd/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request)

if req.Name == database.EveryoneGroup {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: fmt.Sprintf("%q is a reserved keyword and cannot be used for a group name.", database.EveryoneGroup),
Message: "Invalid group name.",
Validations: []codersdk.ValidationError{{Field: "name", Detail: fmt.Sprintf("%q is a reserved group name", req.Name)}},
})
return
}
Expand All @@ -63,7 +64,8 @@ func (api *API) postGroupByOrganization(rw http.ResponseWriter, r *http.Request)
})
if database.IsUniqueViolation(err) {
httpapi.Write(ctx, rw, http.StatusConflict, codersdk.Response{
Message: fmt.Sprintf("Group with name %q already exists.", req.Name),
Message: fmt.Sprintf("A group named %q already exists.", req.Name),
Validations: []codersdk.ValidationError{{Field: "name", Detail: "Group names must be unique"}},
})
return
}
Expand Down
2 changes: 1 addition & 1 deletion site/src/pages/GroupsPage/CreateGroupPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const CreateGroupPage: FC = () => {
});
navigate(`/groups/${newGroup.id}`);
}}
formErrors={createGroupMutation.error}
error={createGroupMutation.error}
isLoading={createGroupMutation.isLoading}
/>
</>
Expand Down
13 changes: 11 additions & 2 deletions site/src/pages/GroupsPage/CreateGroupPageView.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { mockApiError } from "testHelpers/entities";
import { CreateGroupPageView } from "./CreateGroupPageView";
import type { Meta, StoryObj } from "@storybook/react";

Expand All @@ -9,6 +10,14 @@ const meta: Meta<typeof CreateGroupPageView> = {
export default meta;
type Story = StoryObj<typeof CreateGroupPageView>;

const Example: Story = {};
export const Example: Story = {};

export { Example as CreateGroupPageView };
export const WithError: Story = {
args: {
error: mockApiError({
message: "A group named new-group already exists.",
validations: [{ field: "name", detail: "Group names must be unique" }],
}),
initialTouched: { name: true },
},
};
26 changes: 18 additions & 8 deletions site/src/pages/GroupsPage/CreateGroupPageView.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import TextField from "@mui/material/TextField";
import { CreateGroupRequest } from "api/typesGenerated";
import { type FormikTouched, useFormik } from "formik";
import { type FC } from "react";
import { useNavigate } from "react-router-dom";
import * as Yup from "yup";
import type { CreateGroupRequest } from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { FormFooter } from "components/FormFooter/FormFooter";
import { FullPageForm } from "components/FullPageForm/FullPageForm";
import { IconField } from "components/IconField/IconField";
import { Margins } from "components/Margins/Margins";
import { Stack } from "components/Stack/Stack";
import { useFormik } from "formik";
import { FC } from "react";
import { useNavigate } from "react-router-dom";
import { getFormHelpers, onChangeTrimmed } from "utils/formUtils";
import * as Yup from "yup";
import { isApiValidationError } from "api/errors";

const validationSchema = Yup.object({
name: Yup.string().required().label("Name"),
});

export type CreateGroupPageViewProps = {
onSubmit: (data: CreateGroupRequest) => void;
formErrors?: unknown;
error?: unknown;
isLoading: boolean;
// Helpful to show field errors on Storybook
initialTouched?: FormikTouched<CreateGroupRequest>;
};

export const CreateGroupPageView: FC<CreateGroupPageViewProps> = ({
onSubmit,
formErrors,
error,
isLoading,
initialTouched,
}) => {
const navigate = useNavigate();
const form = useFormik<CreateGroupRequest>({
Expand All @@ -36,15 +41,20 @@ export const CreateGroupPageView: FC<CreateGroupPageViewProps> = ({
},
validationSchema,
onSubmit,
initialTouched,
});
const getFieldHelpers = getFormHelpers<CreateGroupRequest>(form, formErrors);
const getFieldHelpers = getFormHelpers<CreateGroupRequest>(form, error);
const onCancel = () => navigate("/groups");

return (
<Margins>
<FullPageForm title="Create group">
<form onSubmit={form.handleSubmit}>
<Stack spacing={2.5}>
{Boolean(error) && !isApiValidationError(error) && (
<ErrorAlert error={error} />
)}

<TextField
{...getFieldHelpers("name")}
autoFocus
Expand Down
8 changes: 4 additions & 4 deletions site/src/testHelpers/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1946,17 +1946,17 @@ type MockAPIOutput = {
};

export const mockApiError = ({
message,
message = "Something went wrong.",
detail,
validations,
}: MockAPIInput): MockAPIOutput => ({
// This is how axios can check if it is an axios error when calling isAxiosError
isAxiosError: true,
response: {
data: {
message: message ?? "Something went wrong.",
detail: detail ?? undefined,
validations: validations ?? undefined,
message,
detail,
validations,
},
},
});
Expand Down