mirror of
https://github.com/graphql-hive/console
synced 2026-05-23 17:18:23 +00:00
Drop modals from V2: Create target modal (#5227)
This commit is contained in:
parent
5e59354157
commit
479397e570
33 changed files with 275 additions and 156 deletions
|
|
@ -2,8 +2,6 @@ import { ReactElement, ReactNode } from 'react';
|
|||
import { useQuery } from 'urql';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { UserMenu } from '@/components/ui/user-menu';
|
||||
import { HiveLink } from '@/components/v2/hive-link';
|
||||
import { PlusIcon } from '@/components/v2/icon';
|
||||
import { CreateProjectModal } from '@/components/v2/modals';
|
||||
import { Tabs } from '@/components/v2/tabs';
|
||||
import { env } from '@/env/frontend';
|
||||
|
|
@ -19,6 +17,8 @@ import { useLastVisitedOrganizationWriter } from '@/lib/last-visited-org';
|
|||
import { Link } from '@tanstack/react-router';
|
||||
import { ProPlanBilling } from '../organization/billing/ProPlanBillingWarm';
|
||||
import { RateLimitWarn } from '../organization/billing/RateLimitWarn';
|
||||
import { HiveLink } from '../ui/hive-link';
|
||||
import { PlusIcon } from '../ui/icon';
|
||||
import { QueryError } from '../ui/query-error';
|
||||
import { OrganizationSelector } from './organization-selectors';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@ import { ReactElement, ReactNode } from 'react';
|
|||
import { useQuery } from 'urql';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { UserMenu } from '@/components/ui/user-menu';
|
||||
import { HiveLink } from '@/components/v2/hive-link';
|
||||
import { PlusIcon } from '@/components/v2/icon';
|
||||
import { CreateTargetModal } from '@/components/v2/modals';
|
||||
import { Tabs } from '@/components/v2/tabs';
|
||||
import { graphql } from '@/gql';
|
||||
import { canAccessProject, ProjectAccessScope, useProjectAccess } from '@/lib/access/project';
|
||||
|
|
@ -12,6 +9,9 @@ import { useToggle } from '@/lib/hooks';
|
|||
import { useLastVisitedOrganizationWriter } from '@/lib/last-visited-org';
|
||||
import { Link } from '@tanstack/react-router';
|
||||
import { ProjectMigrationToast } from '../project/migration-toast';
|
||||
import { CreateTargetModal } from '../ui/create-target';
|
||||
import { HiveLink } from '../ui/hive-link';
|
||||
import { PlusIcon } from '../ui/icon';
|
||||
import { ProjectSelector } from './project-selector';
|
||||
|
||||
export enum Page {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { ReactElement, ReactNode } from 'react';
|
|||
import { LinkIcon } from 'lucide-react';
|
||||
import { useQuery } from 'urql';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { HiveLink } from '@/components/ui/hive-link';
|
||||
import { UserMenu } from '@/components/ui/user-menu';
|
||||
import { HiveLink } from '@/components/v2/hive-link';
|
||||
import { ConnectSchemaModal } from '@/components/v2/modals';
|
||||
import { Tabs } from '@/components/v2/tabs';
|
||||
import { graphql } from '@/gql';
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ import {
|
|||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { AlertTriangleIcon, KeyIcon } from '@/components/ui/icon';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { Tag } from '@/components/v2';
|
||||
import { AlertTriangleIcon, KeyIcon } from '@/components/v2/icon';
|
||||
import { env } from '@/env/frontend';
|
||||
import { DocumentType, FragmentType, graphql, useFragment } from '@/gql';
|
||||
import { useClipboard } from '@/lib/hooks';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React, { ReactElement, ReactNode, useMemo } from 'react';
|
||||
import { clsx } from 'clsx';
|
||||
import { PulseIcon, UsersIcon } from '@/components/ui/icon';
|
||||
import { Popover, PopoverArrow, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { PulseIcon, UsersIcon } from '@/components/v2/icon';
|
||||
import { Markdown } from '@/components/v2/markdown';
|
||||
import { FragmentType, graphql, useFragment } from '@/gql';
|
||||
import { formatNumber, toDecimal } from '@/lib/hooks';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useMemo } from 'react';
|
||||
import { PackageIcon } from '@/components/ui/icon';
|
||||
import { Tooltip } from '@/components/v2';
|
||||
import { PackageIcon } from '@/components/v2/icon';
|
||||
import { FragmentType, graphql, useFragment } from '@/gql';
|
||||
import { TooltipProvider } from '@radix-ui/react-tooltip';
|
||||
import { Link } from '@tanstack/react-router';
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
} from '@/components/ui/accordion';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Heading } from '@/components/ui/heading';
|
||||
import { PulseIcon } from '@/components/ui/icon';
|
||||
import { Popover, PopoverArrow, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import {
|
||||
Table,
|
||||
|
|
@ -24,7 +25,6 @@ import {
|
|||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { PulseIcon } from '@/components/v2/icon';
|
||||
import { FragmentType, graphql, useFragment } from '@/gql';
|
||||
import { CriticalityLevel } from '@/gql/graphql';
|
||||
import { CheckCircledIcon, InfoCircledIcon } from '@radix-ui/react-icons';
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import { z } from 'zod';
|
|||
import { Button } from '@/components/ui/button';
|
||||
import { CardDescription } from '@/components/ui/card';
|
||||
import { Heading } from '@/components/ui/heading';
|
||||
import { AlertTriangleIcon, TrashIcon } from '@/components/ui/icon';
|
||||
import { SubPageLayout, SubPageLayoutHeader } from '@/components/ui/page-content-layout';
|
||||
import { DocsLink, Input, Modal, Table, Tag, TBody, Td, TimeAgo, Tr } from '@/components/v2';
|
||||
import { AlertTriangleIcon, TrashIcon } from '@/components/v2/icon';
|
||||
import { InlineCode } from '@/components/v2/inline-code';
|
||||
import { FragmentType, graphql, useFragment } from '@/gql';
|
||||
import { TargetAccessScope } from '@/gql/graphql';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ReactElement, ReactNode } from 'react';
|
||||
import { useQuery } from 'urql';
|
||||
import { EditIcon, PlusIcon, TrashIcon, UserPlusMinusIcon } from '@/components/v2/icon';
|
||||
import { EditIcon, PlusIcon, TrashIcon, UserPlusMinusIcon } from '@/components/ui/icon';
|
||||
import { DocumentType, graphql, useFragment } from '@/gql';
|
||||
import { Link } from './link';
|
||||
import { Subtitle, Title } from './page';
|
||||
|
|
|
|||
182
packages/web/app/src/components/ui/create-target.tsx
Normal file
182
packages/web/app/src/components/ui/create-target.tsx
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
import { ReactElement } from 'react';
|
||||
import { useForm, UseFormReturn } from 'react-hook-form';
|
||||
import { useMutation } from 'urql';
|
||||
import { z } from 'zod';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Form, FormControl, FormField, FormItem, FormMessage } from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { graphql } from '@/gql';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useRouter } from '@tanstack/react-router';
|
||||
|
||||
export const CreateTarget_CreateTargetMutation = graphql(`
|
||||
mutation CreateTarget_CreateTarget($input: CreateTargetInput!) {
|
||||
createTarget(input: $input) {
|
||||
ok {
|
||||
selector {
|
||||
organization
|
||||
project
|
||||
target
|
||||
}
|
||||
createdTarget {
|
||||
id
|
||||
cleanId
|
||||
name
|
||||
}
|
||||
}
|
||||
error {
|
||||
message
|
||||
inputErrors {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const formSchema = z.object({
|
||||
targetName: z
|
||||
.string({
|
||||
required_error: 'Target name is required',
|
||||
})
|
||||
.min(2, {
|
||||
message: 'Target name must be at least 2 characters long',
|
||||
})
|
||||
.max(50, {
|
||||
message: 'Target name must be at most 50 characters long',
|
||||
})
|
||||
.regex(
|
||||
/^([a-z]|[0-9]|\s|\.|,|_|-|\/|&)+$/i,
|
||||
'Target name restricted to alphanumerical characters, spaces and . , _ - / &',
|
||||
),
|
||||
});
|
||||
|
||||
type CreateTargetModalProps = {
|
||||
isOpen: boolean;
|
||||
toggleModalOpen: () => void;
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
export const CreateTargetModal = ({ ...props }: CreateTargetModalProps): ReactElement => {
|
||||
const { organizationId, projectId } = props;
|
||||
const [_, mutate] = useMutation(CreateTarget_CreateTargetMutation);
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
mode: 'onChange',
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
targetName: '',
|
||||
},
|
||||
});
|
||||
|
||||
async function onSubmit(values: z.infer<typeof formSchema>) {
|
||||
const { data, error } = await mutate({
|
||||
input: {
|
||||
project: props.projectId,
|
||||
organization: props.organizationId,
|
||||
name: values.targetName,
|
||||
},
|
||||
});
|
||||
|
||||
if (data?.createTarget.ok) {
|
||||
props.toggleModalOpen();
|
||||
void router.navigate({
|
||||
to: '/$organizationId/$projectId/$targetId',
|
||||
params: {
|
||||
organizationId,
|
||||
projectId,
|
||||
targetId: data.createTarget.ok.createdTarget.cleanId,
|
||||
},
|
||||
});
|
||||
toast({
|
||||
variant: 'default',
|
||||
title: 'Target created',
|
||||
description: `Your target "${data.createTarget.ok.createdTarget.name}" has been created`,
|
||||
});
|
||||
} else if (data?.createTarget.error?.inputErrors.name) {
|
||||
form.setError('targetName', {
|
||||
message: data?.createTarget.error?.inputErrors.name,
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
title: 'Failed to create target',
|
||||
description: error?.message || data?.createTarget.error?.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<CreateTargetModalContent
|
||||
form={form}
|
||||
isOpen={props.isOpen}
|
||||
onSubmit={onSubmit}
|
||||
toggleModalOpen={props.toggleModalOpen}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type CreateTargetModalContentProps = {
|
||||
isOpen: boolean;
|
||||
toggleModalOpen: () => void;
|
||||
onSubmit: (values: z.infer<typeof formSchema>) => void | Promise<void>;
|
||||
form: UseFormReturn<z.infer<typeof formSchema>>;
|
||||
};
|
||||
|
||||
export const CreateTargetModalContent = ({
|
||||
...props
|
||||
}: CreateTargetModalContentProps): ReactElement => {
|
||||
return (
|
||||
<Dialog open={props.isOpen} onOpenChange={props.toggleModalOpen}>
|
||||
<DialogContent className="container w-4/5 max-w-[520px] md:w-3/5">
|
||||
<Form {...props.form}>
|
||||
<form className="space-y-8" onSubmit={props.form.handleSubmit(props.onSubmit)}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create a new target</DialogTitle>
|
||||
<DialogDescription>
|
||||
A project is built on top of <b>Targets</b>, which are just your environments.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-8">
|
||||
<FormField
|
||||
control={props.form.control}
|
||||
name="targetName"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input placeholder="Target name" autoComplete="off" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
className="w-full"
|
||||
type="submit"
|
||||
disabled={props.form.formState.isSubmitting || !props.form.formState.isValid}
|
||||
>
|
||||
{props.form.formState.isSubmitting ? 'Submitting...' : 'Create Target'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { ReactElement } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { HiveLogo } from '@/components/v2/icon';
|
||||
import { Link } from '@tanstack/react-router';
|
||||
import { HiveLogo } from './icon';
|
||||
|
||||
export const HiveLink = ({ className }: { className?: string }): ReactElement => {
|
||||
return (
|
||||
|
|
@ -9,7 +9,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'border-input bg-background placeholder:text-muted-foreground flex h-10 w-full rounded-md border px-3 py-2 text-sm ring-offset-orange-900 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-transparent focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import {
|
|||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Avatar } from '@/components/v2';
|
||||
import {
|
||||
AlertTriangleIcon,
|
||||
CalendarIcon,
|
||||
|
|
@ -22,7 +21,8 @@ import {
|
|||
PlusIcon,
|
||||
SettingsIcon,
|
||||
TrendingUpIcon,
|
||||
} from '@/components/v2/icon';
|
||||
} from '@/components/ui/icon';
|
||||
import { Avatar } from '@/components/v2';
|
||||
import { LeaveOrganizationModal } from '@/components/v2/modals/leave-organization';
|
||||
import { env } from '@/env/frontend';
|
||||
import { FragmentType, graphql, useFragment } from '@/gql';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ReactElement, useCallback, useEffect, useState } from 'react';
|
||||
import { CheckIcon, CopyIcon } from '@/components/ui/icon';
|
||||
import { Input } from '@/components/v2';
|
||||
import { CheckIcon, CopyIcon } from '@/components/v2/icon';
|
||||
import { useClipboard } from '@/lib/hooks';
|
||||
import { Button } from '../ui/button';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useNotifications } from '@/lib/hooks';
|
||||
import { CopyIcon } from './icon';
|
||||
import { CopyIcon } from '../ui/icon';
|
||||
|
||||
export const InlineCode = (props: { content: string }) => {
|
||||
const post = useNotifications();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createContext, ReactElement, ReactNode, useState } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { XIcon } from '@/components/v2/icon';
|
||||
import { XIcon } from '@/components/ui/icon';
|
||||
import {
|
||||
Close,
|
||||
Content,
|
||||
|
|
|
|||
|
|
@ -1,123 +0,0 @@
|
|||
import { ReactElement } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import { useMutation } from 'urql';
|
||||
import * as Yup from 'yup';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Heading } from '@/components/ui/heading';
|
||||
import { Input, Modal } from '@/components/v2';
|
||||
import { graphql } from '@/gql';
|
||||
import { useRouter } from '@tanstack/react-router';
|
||||
|
||||
export const CreateTarget_CreateTargetMutation = graphql(`
|
||||
mutation CreateTarget_CreateTarget($input: CreateTargetInput!) {
|
||||
createTarget(input: $input) {
|
||||
ok {
|
||||
selector {
|
||||
organization
|
||||
project
|
||||
target
|
||||
}
|
||||
createdTarget {
|
||||
id
|
||||
cleanId
|
||||
name
|
||||
}
|
||||
}
|
||||
error {
|
||||
message
|
||||
inputErrors {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
export const CreateTargetModal = (props: {
|
||||
isOpen: boolean;
|
||||
toggleModalOpen: () => void;
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
}): ReactElement => {
|
||||
const { isOpen, toggleModalOpen } = props;
|
||||
const [mutation, mutate] = useMutation(CreateTarget_CreateTargetMutation);
|
||||
const router = useRouter();
|
||||
|
||||
const { handleSubmit, values, handleChange, handleBlur, isSubmitting, errors, touched } =
|
||||
useFormik({
|
||||
initialValues: { name: '' },
|
||||
validationSchema: Yup.object().shape({
|
||||
name: Yup.string().required('Target name is required'),
|
||||
}),
|
||||
async onSubmit(values) {
|
||||
const { projectId, organizationId } = props;
|
||||
const { data } = await mutate({
|
||||
input: {
|
||||
project: projectId,
|
||||
organization: organizationId,
|
||||
name: values.name,
|
||||
},
|
||||
});
|
||||
if (data?.createTarget.ok) {
|
||||
toggleModalOpen();
|
||||
const targetId = data.createTarget.ok.createdTarget.cleanId;
|
||||
void router.navigate({
|
||||
to: '/$organizationId/$projectId/$targetId',
|
||||
params: {
|
||||
organizationId,
|
||||
projectId,
|
||||
targetId,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal open={isOpen} onOpenChange={toggleModalOpen}>
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-5">
|
||||
<Heading className="text-center">Create a new target</Heading>
|
||||
<p className="text-sm text-gray-500">
|
||||
A project is build on top of <b>Targets</b>, which are just your environments.
|
||||
</p>
|
||||
<Input
|
||||
placeholder="Target name"
|
||||
name="name"
|
||||
value={values.name}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
disabled={isSubmitting}
|
||||
isInvalid={touched.name && !!errors.name}
|
||||
className="grow"
|
||||
/>
|
||||
{touched.name && (errors.name || mutation.error) && (
|
||||
<div className="-mt-2 text-sm text-red-500">{errors.name || mutation.error?.message}</div>
|
||||
)}
|
||||
{mutation.data?.createTarget.error?.inputErrors.name && (
|
||||
<div className="-mt-2 text-sm text-red-500">
|
||||
{mutation.data.createTarget.error.inputErrors.name}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
size="lg"
|
||||
className="w-full justify-center"
|
||||
onClick={toggleModalOpen}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
size="lg"
|
||||
className="w-full justify-center"
|
||||
variant="primary"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Create Target
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
@ -2,7 +2,6 @@ export { ChangePermissionsModal } from './change-permissions';
|
|||
export { ConnectSchemaModal } from './connect-schema';
|
||||
export { CreateAccessTokenModal } from './create-access-token';
|
||||
export { CreateProjectModal } from './create-project';
|
||||
export { CreateTargetModal } from './create-target';
|
||||
export { DeleteOrganizationModal } from './delete-organization';
|
||||
export { DeleteProjectModal } from './delete-project';
|
||||
export { DeleteTargetModal } from './delete-target';
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import { useMutation, useQuery } from 'urql';
|
|||
import * as Yup from 'yup';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Heading } from '@/components/ui/heading';
|
||||
import { ArrowDownIcon, CheckIcon } from '@/components/ui/icon';
|
||||
import { Input, Modal } from '@/components/v2';
|
||||
import { ArrowDownIcon, CheckIcon } from '@/components/v2/icon';
|
||||
import { FragmentType, graphql, useFragment } from '@/gql';
|
||||
import { useNotifications } from '@/lib/hooks';
|
||||
import { Combobox as HeadlessCombobox, Transition as HeadlessTransition } from '@headlessui/react';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ComponentProps, ReactElement } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { ArrowDownIcon } from '@/components/v2/icon';
|
||||
import { ArrowDownIcon } from '@/components/ui/icon';
|
||||
|
||||
export function Select({
|
||||
options,
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import type { DeleteChannelsButton_DeleteChannelsMutation } from '@/components/p
|
|||
import type { CreateOperationMutationType } from '@/components/target/laboratory/create-operation-modal';
|
||||
import type { DeleteCollectionMutationType } from '@/components/target/laboratory/delete-collection-modal';
|
||||
import type { DeleteOperationMutationType } from '@/components/target/laboratory/delete-operation-modal';
|
||||
import { CreateTarget_CreateTargetMutation } from '@/components/ui/create-target';
|
||||
import type { CreateAccessToken_CreateTokenMutation } from '@/components/v2/modals/create-access-token';
|
||||
import type { CreateProjectMutation } from '@/components/v2/modals/create-project';
|
||||
import type { CreateTarget_CreateTargetMutation } from '@/components/v2/modals/create-target';
|
||||
import type { DeleteOrganizationDocument } from '@/components/v2/modals/delete-organization';
|
||||
import { type DeleteProjectMutation } from '@/components/v2/modals/delete-project';
|
||||
import { type DeleteTargetMutation } from '@/components/v2/modals/delete-target';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { BookIcon } from 'lucide-react';
|
||||
import { SiGithub } from 'react-icons/si';
|
||||
import { useSessionContext } from 'supertokens-auth-react/recipe/session';
|
||||
import { HiveLogo } from '@/components/ui/icon';
|
||||
import { Meta } from '@/components/ui/meta';
|
||||
import { HiveLogo } from '@/components/v2/icon';
|
||||
import { Outlet } from '@tanstack/react-router';
|
||||
|
||||
function ExternalLink(props: { href: string; children: React.ReactNode }) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { GraphiQL } from 'graphiql';
|
||||
import { HiveLogo } from '@/components/v2/icon';
|
||||
import { HiveLogo } from '@/components/ui/icon';
|
||||
import { createGraphiQLFetcher } from '@graphiql/toolkit';
|
||||
import 'graphiql/graphiql.css';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useQuery } from 'urql';
|
||||
import { HiveLogo } from '@/components/ui/icon';
|
||||
import { Meta } from '@/components/ui/meta';
|
||||
import { QueryError } from '@/components/ui/query-error';
|
||||
import { HiveLogo } from '@/components/v2/icon';
|
||||
import { graphql } from '@/gql';
|
||||
import {
|
||||
useLastVisitedOrganizationReader,
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import { useMutation, useQuery } from 'urql';
|
|||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { DottedBackground } from '@/components/ui/dotted-background';
|
||||
import { HiveLogo } from '@/components/ui/icon';
|
||||
import { Meta } from '@/components/ui/meta';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { DataWrapper } from '@/components/v2/data-wrapper';
|
||||
import { HiveLogo } from '@/components/v2/icon';
|
||||
import { graphql } from '@/gql';
|
||||
import { Link, useRouter } from '@tanstack/react-router';
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ import {
|
|||
} from '@/components/ui/card';
|
||||
import { DottedBackground } from '@/components/ui/dotted-background';
|
||||
import { Form, FormControl, FormField, FormItem, FormMessage } from '@/components/ui/form';
|
||||
import { HiveLogo } from '@/components/ui/icon';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Meta } from '@/components/ui/meta';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { HiveLogo } from '@/components/v2/icon';
|
||||
import { graphql } from '@/gql';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Link, useRouter } from '@tanstack/react-router';
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ import {
|
|||
} from '@/components/ui/card';
|
||||
import { DocsLink } from '@/components/ui/docs-note';
|
||||
import { Form, FormControl, FormField, FormItem, FormMessage } from '@/components/ui/form';
|
||||
import { GitHubIcon, SlackIcon } from '@/components/ui/icon';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Meta } from '@/components/ui/meta';
|
||||
import { Subtitle, Title } from '@/components/ui/page';
|
||||
import { QueryError } from '@/components/ui/query-error';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { GitHubIcon, SlackIcon } from '@/components/v2/icon';
|
||||
import {
|
||||
DeleteOrganizationModal,
|
||||
TransferOrganizationOwnershipModal,
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ import {
|
|||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import { DocsLink } from '@/components/ui/docs-note';
|
||||
import { HiveLogo } from '@/components/ui/icon';
|
||||
import { Meta } from '@/components/ui/meta';
|
||||
import { Subtitle, Title } from '@/components/ui/page';
|
||||
import { QueryError } from '@/components/ui/query-error';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { HiveLogo } from '@/components/v2/icon';
|
||||
import { Input } from '@/components/v2/input';
|
||||
import { DeleteProjectModal } from '@/components/v2/modals';
|
||||
import { graphql, useFragment } from '@/gql';
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { Button } from '@/components/ui/button';
|
|||
import { DocsLink } from '@/components/ui/docs-note';
|
||||
import { EmptyList } from '@/components/ui/empty-list';
|
||||
import { Heading } from '@/components/ui/heading';
|
||||
import { AlertTriangleIcon, DiffIcon } from '@/components/ui/icon';
|
||||
import { Meta } from '@/components/ui/meta';
|
||||
import { Subtitle, Title } from '@/components/ui/page';
|
||||
import { Popover, PopoverArrow, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
|
|
@ -23,7 +24,6 @@ import { Textarea } from '@/components/ui/textarea';
|
|||
import { TimeAgo } from '@/components/ui/time-ago';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { DiffEditor } from '@/components/v2/diff-editor';
|
||||
import { AlertTriangleIcon, DiffIcon } from '@/components/v2/icon';
|
||||
import { FragmentType, graphql, useFragment } from '@/gql';
|
||||
import { CriticalityLevel, ProjectType } from '@/gql/graphql';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@ import {
|
|||
CompositionErrorsSection,
|
||||
NoGraphChanges,
|
||||
} from '@/components/target/history/errors-and-changes';
|
||||
import { DiffIcon } from '@/components/ui/icon';
|
||||
import { Subtitle, Title } from '@/components/ui/page';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { DiffEditor } from '@/components/v2';
|
||||
import { DiffIcon } from '@/components/v2/icon';
|
||||
import { FragmentType, graphql, useFragment } from '@/gql';
|
||||
import { CriticalityLevel, ProjectType } from '@/gql/graphql';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
|
|
|||
|
|
@ -29,12 +29,12 @@ import {
|
|||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { PlusIcon, SaveIcon, ShareIcon } from '@/components/ui/icon';
|
||||
import { Link } from '@/components/ui/link';
|
||||
import { Meta } from '@/components/ui/meta';
|
||||
import { Subtitle, Title } from '@/components/ui/page';
|
||||
import { QueryError } from '@/components/ui/query-error';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { PlusIcon, SaveIcon, ShareIcon } from '@/components/v2/icon';
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/components/v2/toggle-group';
|
||||
import { graphql } from '@/gql';
|
||||
import { TargetAccessScope } from '@/gql/graphql';
|
||||
|
|
|
|||
61
packages/web/app/src/stories/create-target.stories.tsx
Normal file
61
packages/web/app/src/stories/create-target.stories.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CreateTargetModalContent } from '@/components/ui/create-target';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
const meta: Meta<typeof CreateTargetModalContent> = {
|
||||
title: 'Modals/Create Target Modal',
|
||||
component: CreateTargetModalContent,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CreateTargetModalContent>;
|
||||
|
||||
const formSchema = z.object({
|
||||
targetName: z
|
||||
.string({
|
||||
required_error: 'Target name is required',
|
||||
})
|
||||
.min(2, {
|
||||
message: 'Target name must be at least 2 characters long',
|
||||
})
|
||||
.max(50, {
|
||||
message: 'Target name must be at most 50 characters long',
|
||||
})
|
||||
.regex(
|
||||
/^([a-z]|[0-9]|\s|\.|,|_|-|\/|&)+$/i,
|
||||
'Target name restricted to alphanumerical characters, spaces and . , _ - / &',
|
||||
),
|
||||
});
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
mode: 'onChange',
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
targetName: '',
|
||||
},
|
||||
});
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const toggleModalOpen = () => setOpenModal(!openModal);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={toggleModalOpen}>Open Modal</Button>
|
||||
{openModal && (
|
||||
<CreateTargetModalContent
|
||||
isOpen={openModal}
|
||||
form={form}
|
||||
onSubmit={() => console.log('Submit')}
|
||||
toggleModalOpen={toggleModalOpen}
|
||||
key="create-target-modal"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
};
|
||||
Loading…
Reference in a new issue