From 20bd2ca71c09b9917b26985cb843102fedcba0f7 Mon Sep 17 00:00:00 2001 From: carlosmonastyrski Date: Mon, 10 Mar 2025 08:18:43 -0300 Subject: [PATCH] Improve slug description, regex and replace useState with watch --- backend/src/lib/api-docs/constants.ts | 2 +- backend/src/server/routes/v1/project-router.ts | 4 ++++ .../project/ProjectOverviewChangeSection.tsx | 18 ++++++++---------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 821f91ddc..94076cf25 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -460,7 +460,7 @@ export const PROJECTS = { name: "The new name of the project.", projectDescription: "An optional description label for the project.", autoCapitalization: "Disable or enable auto-capitalization for the project.", - slug: "An optional slug for the project. (must be unique within the server)" + slug: "An optional slug for the project. (must be unique within the organization)" }, GET_KEY: { workspaceId: "The ID of the project to get the key from." diff --git a/backend/src/server/routes/v1/project-router.ts b/backend/src/server/routes/v1/project-router.ts index 5209dadcf..dcedf5fa5 100644 --- a/backend/src/server/routes/v1/project-router.ts +++ b/backend/src/server/routes/v1/project-router.ts @@ -311,6 +311,10 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { slug: z .string() .trim() + .regex( + /^[a-z0-9]+(?:[_-][a-z0-9]+)*$/, + "Project slug can only contain lowercase letters and numbers, with optional single hyphens (-) or underscores (_) between words. Cannot start or end with a hyphen or underscore." + ) .max(64, { message: "Slug must be 64 characters or fewer" }) .optional() .describe(PROJECTS.UPDATE.slug) diff --git a/frontend/src/components/project/ProjectOverviewChangeSection.tsx b/frontend/src/components/project/ProjectOverviewChangeSection.tsx index 767f17334..0f88ec2e9 100644 --- a/frontend/src/components/project/ProjectOverviewChangeSection.tsx +++ b/frontend/src/components/project/ProjectOverviewChangeSection.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; @@ -23,7 +23,10 @@ const formSchemaWithSlug = baseFormSchema.extend({ .string() .min(1, "Required") .max(64, "Too long, maximum length is 64 characters") - .regex(/^[a-zA-Z0-9-]+$/, "Only letters, numbers and hyphens are allowed") + .regex( + /^[a-z0-9]+(?:[_-][a-z0-9]+)*$/, + "Project slug can only contain lowercase letters and numbers, with optional single hyphens (-) or underscores (_) between words. Cannot start or end with a hyphen or underscore." + ) }); type BaseFormData = z.infer; @@ -35,13 +38,13 @@ type Props = { export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) => { const { currentWorkspace } = useWorkspace(); - const [currentSlug, setCurrentSlug] = useState(currentWorkspace?.slug); const { mutateAsync, isPending } = useUpdateProject(); - - const { handleSubmit, control, reset } = useForm({ + const { handleSubmit, control, reset, watch } = useForm({ resolver: zodResolver(showSlugField ? formSchemaWithSlug : baseFormSchema) }); + const currentSlug = showSlugField ? watch("slug") : currentWorkspace?.slug; + useEffect(() => { if (currentWorkspace) { reset({ @@ -49,7 +52,6 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) = description: currentWorkspace.description ?? "", ...(showSlugField && { slug: currentWorkspace.slug }) }); - setCurrentSlug(currentWorkspace.slug); } }, [currentWorkspace, showSlugField]); @@ -67,10 +69,6 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) = }) }); - if (showSlugField && "slug" in data) { - setCurrentSlug(data.slug); - } - createNotification({ text: "Successfully updated project overview", type: "success"