From f4a87c66b360cffd0879ba4d809354c39dd48305 Mon Sep 17 00:00:00 2001 From: Leo Giovanetti Date: Fri, 23 Jun 2023 17:01:54 -0300 Subject: [PATCH] Adding team and org registration suggestion (#9764) --- apps/web/pages/404.tsx | 130 ++++++++++++------ apps/web/public/static/locales/en/common.json | 8 +- .../components/CreateANewOrganizationForm.tsx | 11 +- .../teams/components/CreateANewTeamForm.tsx | 14 +- 4 files changed, 114 insertions(+), 49 deletions(-) diff --git a/apps/web/pages/404.tsx b/apps/web/pages/404.tsx index 3a915fdb7b..a609a36524 100644 --- a/apps/web/pages/404.tsx +++ b/apps/web/pages/404.tsx @@ -3,22 +3,37 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { useEffect, useState } from "react"; +import { orgDomainConfig, subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains"; import { DOCS_URL, JOIN_SLACK, WEBSITE_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { HeadSeo } from "@calcom/ui"; -import { BookOpen, Check, ChevronRight, FileText, Slack } from "@calcom/ui/components/icon"; +import { BookOpen, Check, ChevronRight, FileText, Slack, Shield } from "@calcom/ui/components/icon"; import PageWrapper from "@components/PageWrapper"; import { ssgInit } from "@server/lib/ssg"; +enum pageType { + ORG = "org", + TEAM = "team", + USER = "user", + OTHER = "other", +} + export default function Custom404() { const { t } = useLocale(); const router = useRouter(); - const [username] = router.asPath.replace("%20", "-").split(/[?#]/); + const [username, setUsername] = useState(""); + const [currentPageType, setCurrentPageType] = useState(pageType.USER); const links = [ + { + title: "Enterprise", + description: "Learn more about organizations and subdomains in our enterprise plan.", + icon: Shield, + href: `${WEBSITE_URL}/enterprise`, + }, { title: t("documentation"), description: t("documentation_description"), @@ -33,10 +48,29 @@ export default function Custom404() { }, ]; - const [url, setUrl] = useState(`${WEBSITE_URL}/signup?username=`); + const [url, setUrl] = useState(`${WEBSITE_URL}/signup`); useEffect(() => { - setUrl(`${WEBSITE_URL}/signup?username=${username.replace("/", "")}`); - }, [username]); + const { isValidOrgDomain, currentOrgDomain } = orgDomainConfig(window.location.host); + const [routerUsername] = router.asPath.replace("%20", "-").split(/[?#]/); + if (!isValidOrgDomain || !currentOrgDomain) { + const splitPath = routerUsername.split("/"); + if (splitPath[1] === "team" && splitPath.length === 3) { + // Accessing a non-existent team + setUsername(splitPath[2]); + setCurrentPageType(pageType.TEAM); + setUrl(`${WEBSITE_URL}/signup?callbackUrl=settings/teams/new%3Fslug%3D${username.replace("/", "")}`); + } else { + setUsername(routerUsername); + setUrl(`${WEBSITE_URL}/signup?username=${username.replace("/", "")}`); + } + } else { + setUsername(currentOrgDomain); + setCurrentPageType(pageType.ORG); + setUrl( + `${WEBSITE_URL}/signup?callbackUrl=settings/organizations/new%3Fslug%3D${username.replace("/", "")}` + ); + } + }, []); const isSuccessPage = router.asPath.startsWith("/booking"); const isSubpage = router.asPath.includes("/", 2) || isSuccessPage; @@ -80,6 +114,8 @@ export default function Custom404() { ); } + if (!username) return null; + return ( <> {isSuccessPage ? "Booking not found" : t("page_doesnt_exist")} - {isSubpage ? ( + {isSubpage && currentPageType !== pageType.TEAM ? ( {t("check_spelling_mistakes_or_go_back")} ) : isCalcom ? ( - {t("the_username")}{" "} + {t(`404_the_${currentPageType.toLowerCase()}`)}{" "} {new URL(WEBSITE_URL).hostname} {username} @@ -217,18 +253,17 @@ export default function Custom404() { ) : ( - {t("the_username")}{" "} + {t(`404_the_${currentPageType.toLowerCase()}`)}{" "} {username}{" "} {t("is_still_available")} )}
+
+

+ + +

+

{link.description}

+
+
+
+ + + ))}
  • { const telemetry = useTelemetry(); const [serverErrorMessage, setServerErrorMessage] = useState(null); const [showVerifyCode, setShowVerifyCode] = useState(false); + const { slug } = router.query; const newOrganizationFormMethods = useForm<{ name: string; slug: string; adminEmail: string; adminUsername: string; - }>(); + }>({ + defaultValues: { + slug: `${slug}`, + }, + }); const watchAdminEmail = newOrganizationFormMethods.watch("adminEmail"); const createOrganizationMutation = trpc.viewer.organizations.create.useMutation({ @@ -216,7 +221,9 @@ export const CreateANewOrganizationForm = () => { const domain = extractDomainFromEmail(e?.target.value); newOrganizationFormMethods.setValue("adminEmail", e?.target.value); newOrganizationFormMethods.setValue("adminUsername", e?.target.value.split("@")[0]); - newOrganizationFormMethods.setValue("slug", domain); + if (newOrganizationFormMethods.getValues("slug") === "") { + newOrganizationFormMethods.setValue("slug", domain); + } newOrganizationFormMethods.setValue( "name", domain.charAt(0).toUpperCase() + domain.slice(1) diff --git a/packages/features/ee/teams/components/CreateANewTeamForm.tsx b/packages/features/ee/teams/components/CreateANewTeamForm.tsx index 3eb741a15d..620ca0857a 100644 --- a/packages/features/ee/teams/components/CreateANewTeamForm.tsx +++ b/packages/features/ee/teams/components/CreateANewTeamForm.tsx @@ -15,21 +15,27 @@ import { ArrowRight } from "@calcom/ui/components/icon"; import type { NewTeamFormValues } from "../lib/types"; const querySchema = z.object({ - returnTo: z.string(), + returnTo: z.string().optional(), + slug: z.string().optional(), }); export const CreateANewTeamForm = () => { const { t } = useLocale(); const router = useRouter(); const telemetry = useTelemetry(); - const returnToParsed = querySchema.safeParse(router.query); + const parsedQuery = querySchema.safeParse(router.query); + console.log({ parsedQuery }); const [serverErrorMessage, setServerErrorMessage] = useState(null); const returnToParam = - (returnToParsed.success ? getSafeRedirectUrl(returnToParsed.data.returnTo) : "/settings/teams") || + (parsedQuery.success ? getSafeRedirectUrl(parsedQuery.data.returnTo) : "/settings/teams") || "/settings/teams"; - const newTeamFormMethods = useForm(); + const newTeamFormMethods = useForm({ + defaultValues: { + slug: parsedQuery.success ? parsedQuery.data.slug : "", + }, + }); const createTeamMutation = trpc.viewer.teams.create.useMutation({ onSuccess: (data) => {