Skip to content
Draft
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
21 changes: 7 additions & 14 deletions static/app/components/modals/createTeamModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,13 @@ interface Props extends ModalRenderProps {
function CreateTeamModal({Body, Header, organization, onClose, closeModal}: Props) {
const api = useApi();

const handleSubmit: React.ComponentProps<typeof CreateTeamForm>['onSubmit'] = async (
data,
onSuccess,
onError
) => {
try {
const team: Team = await createTeam(api, data, {orgId: organization.slug});
const handleSubmit: React.ComponentProps<
typeof CreateTeamForm
>['onSubmit'] = async data => {
const team: Team = await createTeam(api, data, {orgId: organization.slug});

closeModal();
onClose?.(team);
onSuccess(team);
} catch (err) {
onError(err as Team);
}
closeModal();
onClose?.(team);
};

return (
Expand All @@ -37,7 +30,7 @@ function CreateTeamModal({Body, Header, organization, onClose, closeModal}: Prop
<h5>{t('Create Team')}</h5>
</Header>
<Body>
<CreateTeamForm organization={organization} onSubmit={handleSubmit} />
<CreateTeamForm onSubmit={handleSubmit} />
</Body>
</Fragment>
);
Expand Down
72 changes: 39 additions & 33 deletions static/app/components/teams/createTeamForm.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,59 @@
import {Fragment} from 'react';
import {z} from 'zod';

import {defaultFormOptions, useScrapsForm} from '@sentry/scraps/form';
import {Flex} from '@sentry/scraps/layout';

import {TextField} from 'sentry/components/forms/fields/textField';
import {Form} from 'sentry/components/forms/form';
import {t} from 'sentry/locale';
import type {Organization, Team} from 'sentry/types/organization';
import type {Team} from 'sentry/types/organization';
import {slugify} from 'sentry/utils/slugify';

type Payload = {
slug: string;
};

type Props = {
onSubmit: (
data: Payload,
onSuccess: (team: Team) => void,
onError: (team: Team) => void
) => void;
organization: Organization;
onSubmit: (data: Payload) => Promise<Team | void> | Team | void;
};

export function CreateTeamForm({organization, onSubmit}: Props) {
const schema = z.object({
slug: z.string().min(1, t('Slug is required')),
});

export function CreateTeamForm({onSubmit}: Props) {
const form = useScrapsForm({
...defaultFormOptions,
defaultValues: {slug: ''},
validators: {onDynamic: schema},
onSubmit: ({value}) => Promise.resolve(onSubmit(value)).catch(() => {}),
});

return (
<Fragment>
<p>
{t('Teams group members for issue assignment, ownership, and notifications.')}
</p>

<Form
submitLabel={t('Create Team')}
apiEndpoint={`/organizations/${organization.slug}/teams/`}
apiMethod="POST"
onSubmit={(data, onSuccess, onError) =>
onSubmit(data as Payload, onSuccess, onError)
}
requireChanges
>
<TextField
stacked
required
name="slug"
label={t('Team Slug')}
transformInput={slugify}
placeholder={t('e.g. operations, web-frontend, mobile-ios')}
help={t('Use lowercase letters, numbers, dashes, and underscores.')}
flexibleControlStateSize
inline={false}
autoFocus
/>
</Form>
<form.AppForm form={form}>
<form.AppField name="slug">
{field => (
<field.Layout.Stack
label={t('Team Slug')}
hintText={t('Use lowercase letters, numbers, dashes, and underscores.')}
required
>
<field.Input
value={field.state.value}
onChange={value => field.handleChange(slugify(value))}
placeholder={t('e.g. operations, web-frontend, mobile-ios')}
autoFocus
/>
</field.Layout.Stack>
)}
</form.AppField>
<Flex justify="end" padding="md 0 0 0">
<form.SubmitButton>{t('Create Team')}</form.SubmitButton>
</Flex>
</form.AppForm>
</Fragment>
);
}
Loading