feat: name removal in tag respective changes in frontend

This commit is contained in:
=
2024-08-07 13:15:53 +05:30
parent 6f4be3e25a
commit 15fb01089b
11 changed files with 50 additions and 49 deletions
@@ -3,6 +3,7 @@ import { Controller, useForm } from "react-hook-form";
import { faCheck } from "@fortawesome/free-solid-svg-icons"; import { faCheck } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import slugify from "@sindresorhus/slugify";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -87,7 +88,13 @@ type Props = {
}; };
const createTagSchema = z.object({ const createTagSchema = z.object({
name: z.string().trim(), slug: z
.string()
.trim()
.toLowerCase()
.refine((v) => slugify(v) === v, {
message: "Invalid slug. Should contain only characters, numbers and hyphen."
}),
color: z.string().trim() color: z.string().trim()
}); });
@@ -123,13 +130,12 @@ export const CreateTagModal = ({ isOpen, onToggle }: Props): JSX.Element => {
if (!isOpen) reset(); if (!isOpen) reset();
}, [isOpen]); }, [isOpen]);
const onFormSubmit = async ({ name, color }: FormData) => { const onFormSubmit = async ({ slug, color }: FormData) => {
try { try {
await createWsTag({ await createWsTag({
workspaceID: workspaceId, workspaceID: workspaceId,
tagName: name,
tagColor: color, tagColor: color,
tagSlug: name.replace(" ", "_") tagSlug: slug
}); });
onToggle(false); onToggle(false);
reset(); reset();
@@ -155,11 +161,11 @@ export const CreateTagModal = ({ isOpen, onToggle }: Props): JSX.Element => {
<form onSubmit={handleSubmit(onFormSubmit)}> <form onSubmit={handleSubmit(onFormSubmit)}>
<Controller <Controller
control={control} control={control}
name="name" name="slug"
defaultValue="" defaultValue=""
render={({ field, fieldState: { error } }) => ( render={({ field, fieldState: { error } }) => (
<FormControl label="Tag Name" isError={Boolean(error)} errorText={error?.message}> <FormControl label="Tag Slug" isError={Boolean(error)} errorText={error?.message}>
<Input {...field} placeholder="Type your tag name" /> <Input {...field} placeholder="Type your tag slug" />
</FormControl> </FormControl>
)} )}
/> />
+1 -2
View File
@@ -28,11 +28,10 @@ export const useCreateWsTag = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<WsTag, {}, CreateTagDTO>({ return useMutation<WsTag, {}, CreateTagDTO>({
mutationFn: async ({ workspaceID, tagName, tagColor, tagSlug }) => { mutationFn: async ({ workspaceID, tagColor, tagSlug }) => {
const { data } = await apiRequest.post<{ workspaceTag: WsTag }>( const { data } = await apiRequest.post<{ workspaceTag: WsTag }>(
`/api/v1/workspace/${workspaceID}/tags`, `/api/v1/workspace/${workspaceID}/tags`,
{ {
name: tagName,
color: tagColor || "", color: tagColor || "",
slug: tagSlug slug: tagSlug
} }
-2
View File
@@ -2,7 +2,6 @@ export type UserWsTags = WsTag[];
export type WsTag = { export type WsTag = {
id: string; id: string;
name: string;
slug: string; slug: string;
color?: string; color?: string;
projectId: string; projectId: string;
@@ -16,7 +15,6 @@ export type WorkspaceTag = { id: string; name: string; slug: string };
export type CreateTagDTO = { export type CreateTagDTO = {
workspaceID: string; workspaceID: string;
tagSlug: string; tagSlug: string;
tagName: string;
tagColor: string; tagColor: string;
}; };
@@ -97,7 +97,7 @@ export const SecretApprovalRequestChangeItem = ({
</Td> </Td>
<Td>{secretVersion?.secretComment}</Td> <Td>{secretVersion?.secretComment}</Td>
<Td> <Td>
{secretVersion?.tags?.map(({ name, id: tagId, color }) => ( {secretVersion?.tags?.map(({ slug, id: tagId, color }) => (
<Tag <Tag
className="flex w-min items-center space-x-2" className="flex w-min items-center space-x-2"
key={`${secretVersion.id}-${tagId}`} key={`${secretVersion.id}-${tagId}`}
@@ -106,7 +106,7 @@ export const SecretApprovalRequestChangeItem = ({
className="h-3 w-3 rounded-full" className="h-3 w-3 rounded-full"
style={{ backgroundColor: color || "#bec2c8" }} style={{ backgroundColor: color || "#bec2c8" }}
/> />
<div className="text-sm">{name}</div> <div className="text-sm">{slug}</div>
</Tag> </Tag>
))} ))}
</Td> </Td>
@@ -119,7 +119,7 @@ export const SecretApprovalRequestChangeItem = ({
</Td> </Td>
<Td>{newVersion?.secretComment}</Td> <Td>{newVersion?.secretComment}</Td>
<Td> <Td>
{newVersion?.tags?.map(({ name, id: tagId, color }) => ( {newVersion?.tags?.map(({ slug, id: tagId, color }) => (
<Tag <Tag
className="flex w-min items-center space-x-2" className="flex w-min items-center space-x-2"
key={`${newVersion.id}-${tagId}`} key={`${newVersion.id}-${tagId}`}
@@ -128,7 +128,7 @@ export const SecretApprovalRequestChangeItem = ({
className="h-3 w-3 rounded-full" className="h-3 w-3 rounded-full"
style={{ backgroundColor: color || "#bec2c8" }} style={{ backgroundColor: color || "#bec2c8" }}
/> />
<div className="text-sm">{name}</div> <div className="text-sm">{slug}</div>
</Tag> </Tag>
))} ))}
</Td> </Td>
@@ -157,7 +157,7 @@ export const SecretApprovalRequestChangeItem = ({
</Td> </Td>
<Td> <Td>
{(op === CommitType.CREATE ? newVersion?.tags : secretVersion?.tags)?.map( {(op === CommitType.CREATE ? newVersion?.tags : secretVersion?.tags)?.map(
({ name, id: tagId, color }) => ( ({ slug, id: tagId, color }) => (
<Tag <Tag
className="flex w-min items-center space-x-2" className="flex w-min items-center space-x-2"
key={`${ key={`${
@@ -168,7 +168,7 @@ export const SecretApprovalRequestChangeItem = ({
className="h-3 w-3 rounded-full" className="h-3 w-3 rounded-full"
style={{ backgroundColor: color || "#bec2c8" }} style={{ backgroundColor: color || "#bec2c8" }}
/> />
<div className="text-sm">{name}</div> <div className="text-sm">{slug}</div>
</Tag> </Tag>
) )
)} )}
@@ -327,7 +327,7 @@ export const ActionBar = ({
</DropdownSubMenuTrigger> </DropdownSubMenuTrigger>
<DropdownSubMenuContent className="rounded-l-none"> <DropdownSubMenuContent className="rounded-l-none">
<DropdownMenuLabel>Apply tags to filter secrets</DropdownMenuLabel> <DropdownMenuLabel>Apply tags to filter secrets</DropdownMenuLabel>
{tags.map(({ id, name, color }) => ( {tags.map(({ id, slug, color }) => (
<DropdownMenuItem <DropdownMenuItem
onClick={(evt) => { onClick={(evt) => {
evt.preventDefault(); evt.preventDefault();
@@ -342,7 +342,7 @@ export const ActionBar = ({
className="mr-2 h-2 w-2 rounded-full" className="mr-2 h-2 w-2 rounded-full"
style={{ background: color || "#bec2c8" }} style={{ background: color || "#bec2c8" }}
/> />
{name} {slug}
</div> </div>
</DropdownMenuItem> </DropdownMenuItem>
))} ))}
@@ -254,7 +254,7 @@ export const SecretDetailSidebar = ({
)} )}
<FormControl label="Tags" className=""> <FormControl label="Tags" className="">
<div className="grid auto-cols-min grid-flow-col gap-2 overflow-hidden pt-2"> <div className="grid auto-cols-min grid-flow-col gap-2 overflow-hidden pt-2">
{fields.map(({ tagColor, id: formId, name, id }) => ( {fields.map(({ tagColor, id: formId, slug, id }) => (
<Tag <Tag
className="flex w-min items-center space-x-2" className="flex w-min items-center space-x-2"
key={formId} key={formId}
@@ -271,7 +271,7 @@ export const SecretDetailSidebar = ({
className="h-3 w-3 rounded-full" className="h-3 w-3 rounded-full"
style={{ backgroundColor: tagColor || "#bec2c8" }} style={{ backgroundColor: tagColor || "#bec2c8" }}
/> />
<div className="text-sm">{name}</div> <div className="text-sm">{slug}</div>
</Tag> </Tag>
))} ))}
<DropdownMenu> <DropdownMenu>
@@ -296,7 +296,7 @@ export const SecretDetailSidebar = ({
<DropdownMenuContent align="end" className="z-[100]"> <DropdownMenuContent align="end" className="z-[100]">
<DropdownMenuLabel>Add tags to this secret</DropdownMenuLabel> <DropdownMenuLabel>Add tags to this secret</DropdownMenuLabel>
{tags.map((tag) => { {tags.map((tag) => {
const { id: tagId, name, color } = tag; const { id: tagId, slug, color } = tag;
const isSelected = selectedTagsGroupById?.[tagId]; const isSelected = selectedTagsGroupById?.[tagId];
return ( return (
@@ -311,7 +311,7 @@ export const SecretDetailSidebar = ({
className="mr-2 h-2 w-2 rounded-full" className="mr-2 h-2 w-2 rounded-full"
style={{ background: color || "#bec2c8" }} style={{ background: color || "#bec2c8" }}
/> />
{name} {slug}
</div> </div>
</DropdownMenuItem> </DropdownMenuItem>
); );
@@ -336,7 +336,7 @@ export const SecretItem = memo(
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
<DropdownMenuLabel>Add tags to this secret</DropdownMenuLabel> <DropdownMenuLabel>Add tags to this secret</DropdownMenuLabel>
{tags.map((tag) => { {tags.map((tag) => {
const { id: tagId, name, color } = tag; const { id: tagId, slug, color } = tag;
const isTagSelected = selectedTagsGroupById?.[tagId]; const isTagSelected = selectedTagsGroupById?.[tagId];
return ( return (
@@ -358,7 +358,7 @@ export const SecretItem = memo(
className="mr-2 h-2 w-2 rounded-full" className="mr-2 h-2 w-2 rounded-full"
style={{ background: color || "#bec2c8" }} style={{ background: color || "#bec2c8" }}
/> />
{name} {slug}
</div> </div>
</DropdownMenuItem> </DropdownMenuItem>
); );
@@ -49,7 +49,6 @@ export const formSchema = z.object({
tags: z tags: z
.object({ .object({
id: z.string(), id: z.string(),
name: z.string(),
slug: z.string(), slug: z.string(),
tagColor: z.string().optional() tagColor: z.string().optional()
}) })
@@ -151,7 +151,7 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => {
<Td className="border-r border-mineshaft-600">Tags</Td> <Td className="border-r border-mineshaft-600">Tags</Td>
{isModified && ( {isModified && (
<Td className="border-r border-mineshaft-600"> <Td className="border-r border-mineshaft-600">
{preSecret?.tags?.map(({ name, id: tagId, color }) => ( {preSecret?.tags?.map(({ slug, id: tagId, color }) => (
<Tag <Tag
className="flex w-min items-center space-x-2" className="flex w-min items-center space-x-2"
key={`${preSecret.id}-${tagId}`} key={`${preSecret.id}-${tagId}`}
@@ -160,13 +160,13 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => {
className="h-3 w-3 rounded-full" className="h-3 w-3 rounded-full"
style={{ backgroundColor: color || "#bec2c8" }} style={{ backgroundColor: color || "#bec2c8" }}
/> />
<div className="text-sm">{name}</div> <div className="text-sm">{slug}</div>
</Tag> </Tag>
))} ))}
</Td> </Td>
)} )}
<Td> <Td>
{postSecret?.tags?.map(({ name, id: tagId, color }) => ( {postSecret?.tags?.map(({ slug, id: tagId, color }) => (
<Tag <Tag
className="flex w-min items-center space-x-2" className="flex w-min items-center space-x-2"
key={`${postSecret.id}-${tagId}`} key={`${postSecret.id}-${tagId}`}
@@ -175,7 +175,7 @@ export const SecretItem = ({ mode, preSecret, postSecret }: Props) => {
className="h-3 w-3 rounded-full" className="h-3 w-3 rounded-full"
style={{ backgroundColor: color || "#bec2c8" }} style={{ backgroundColor: color || "#bec2c8" }}
/> />
<div className="text-sm">{name}</div> <div className="text-sm">{slug}</div>
</Tag> </Tag>
))} ))}
</Td> </Td>
@@ -1,6 +1,7 @@
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup"; import { zodResolver } from "@hookform/resolvers/zod";
import * as yup from "yup"; import slugify from "@sindresorhus/slugify";
import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { Button, FormControl, Input, Modal, ModalClose, ModalContent } from "@app/components/v2"; import { Button, FormControl, Input, Modal, ModalClose, ModalContent } from "@app/components/v2";
@@ -8,11 +9,13 @@ import { useWorkspace } from "@app/context";
import { useCreateWsTag } from "@app/hooks/api"; import { useCreateWsTag } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
const schema = yup.object({ const schema = z.object({
name: yup.string().required().label("Tag Name") slug: z.string().refine((v) => slugify(v) === v, {
message: "Invalid slug. Should contain only characters, numbers and hyphen."
})
}); });
export type FormData = yup.InferType<typeof schema>; export type FormData = z.infer<typeof schema>;
type Props = { type Props = {
popUp: UsePopUpState<["CreateSecretTag", "deleteTagConfirmation"]>; popUp: UsePopUpState<["CreateSecretTag", "deleteTagConfirmation"]>;
@@ -26,7 +29,6 @@ type Props = {
}; };
export const AddSecretTagModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props) => { export const AddSecretTagModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props) => {
const { currentWorkspace } = useWorkspace(); const { currentWorkspace } = useWorkspace();
const createWsTag = useCreateWsTag(); const createWsTag = useCreateWsTag();
const { const {
@@ -35,17 +37,16 @@ export const AddSecretTagModal = ({ popUp, handlePopUpClose, handlePopUpToggle }
handleSubmit, handleSubmit,
formState: { isSubmitting } formState: { isSubmitting }
} = useForm<FormData>({ } = useForm<FormData>({
resolver: yupResolver(schema) resolver: zodResolver(schema)
}); });
const onFormSubmit = async ({ name }: FormData) => { const onFormSubmit = async ({ slug }: FormData) => {
try { try {
if (!currentWorkspace?.id) return; if (!currentWorkspace?.id) return;
await createWsTag.mutateAsync({ await createWsTag.mutateAsync({
workspaceID: currentWorkspace?.id, workspaceID: currentWorkspace?.id,
tagName: name, tagSlug: slug,
tagSlug: name.replace(/\s+/g, " ").replace(" ", "_"),
tagColor: "" tagColor: ""
}); });
@@ -80,11 +81,11 @@ export const AddSecretTagModal = ({ popUp, handlePopUpClose, handlePopUpToggle }
<form onSubmit={handleSubmit(onFormSubmit)}> <form onSubmit={handleSubmit(onFormSubmit)}>
<Controller <Controller
control={control} control={control}
name="name" name="slug"
defaultValue="" defaultValue=""
render={({ field, fieldState: { error } }) => ( render={({ field, fieldState: { error } }) => (
<FormControl label="Tag Name" isError={Boolean(error)} errorText={error?.message}> <FormControl label="Tag Slug" isError={Boolean(error)} errorText={error?.message}>
<Input {...field} placeholder="Type your tag name" /> <Input {...field} placeholder="Type your tag slug" />
</FormControl> </FormControl>
)} )}
/> />
@@ -40,7 +40,6 @@ export const SecretTagsTable = ({ handlePopUpOpen }: Props) => {
<Table> <Table>
<THead> <THead>
<Tr> <Tr>
<Th>Tag</Th>
<Th>Slug</Th> <Th>Slug</Th>
<Th aria-label="button" /> <Th aria-label="button" />
</Tr> </Tr>
@@ -49,9 +48,8 @@ export const SecretTagsTable = ({ handlePopUpOpen }: Props) => {
{isLoading && <TableSkeleton columns={3} innerKey="secret-tags" />} {isLoading && <TableSkeleton columns={3} innerKey="secret-tags" />}
{!isLoading && {!isLoading &&
data && data &&
data.map(({ id, name, slug }) => ( data.map(({ id, slug }) => (
<Tr key={name}> <Tr key={id}>
<Td>{name}</Td>
<Td>{slug}</Td> <Td>{slug}</Td>
<Td className="flex items-center justify-end"> <Td className="flex items-center justify-end">
<ProjectPermissionCan <ProjectPermissionCan
@@ -62,7 +60,7 @@ export const SecretTagsTable = ({ handlePopUpOpen }: Props) => {
<IconButton <IconButton
onClick={() => onClick={() =>
handlePopUpOpen("deleteTagConfirmation", { handlePopUpOpen("deleteTagConfirmation", {
name, name: slug,
id id
}) })
} }