From b8e5376b99a4660af5fa69f5a2d6c1c39efbe32c Mon Sep 17 00:00:00 2001
From: MaheshtheDev <38828053+MaheshtheDev@users.noreply.github.com>
Date: Thu, 16 Apr 2026 16:40:15 +0000
Subject: [PATCH 01/55] fix: input disable when onboarding org creation (#855)
---
.../app/(app)/onboarding/welcome/layout.tsx | 7 +-
.../web/app/(app)/onboarding/welcome/page.tsx | 106 +++++++++---------
apps/web/components/initial-header.tsx | 41 +++++--
.../components/onboarding/setup/header.tsx | 27 ++++-
.../onboarding/welcome/input-step.tsx | 21 +++-
5 files changed, 136 insertions(+), 66 deletions(-)
diff --git a/apps/web/app/(app)/onboarding/welcome/layout.tsx b/apps/web/app/(app)/onboarding/welcome/layout.tsx
index 2b889d38..8a685368 100644
--- a/apps/web/app/(app)/onboarding/welcome/layout.tsx
+++ b/apps/web/app/(app)/onboarding/welcome/layout.tsx
@@ -11,6 +11,7 @@ import {
} from "react"
import { useRouter, useSearchParams } from "next/navigation"
import { useOnboardingContext, type MemoryFormData } from "../layout"
+import { useAuth } from "@lib/auth-context"
import { analytics } from "@/lib/analytics"
export const WELCOME_STEPS = [
@@ -51,13 +52,17 @@ export default function WelcomeLayout({ children }: { children: ReactNode }) {
const searchParams = useSearchParams()
const { name, setName, memoryFormData, setMemoryFormData } =
useOnboardingContext()
+ const { organizations } = useAuth()
+ const hasOrgs = Array.isArray(organizations) && organizations.length > 0
const stepParam = searchParams.get("step")
- const currentStep: WelcomeStep = WELCOME_STEPS.includes(
+ const resolvedStep: WelcomeStep = WELCOME_STEPS.includes(
stepParam as WelcomeStep,
)
? (stepParam as WelcomeStep)
: "input"
+ const currentStep: WelcomeStep =
+ resolvedStep === "input" && hasOrgs ? "greeting" : resolvedStep
const [isSubmitting, setIsSubmitting] = useState(false)
const [showWelcomeContent, setShowWelcomeContent] = useState(false)
diff --git a/apps/web/app/(app)/onboarding/welcome/page.tsx b/apps/web/app/(app)/onboarding/welcome/page.tsx
index e0df2445..ac3ea599 100644
--- a/apps/web/app/(app)/onboarding/welcome/page.tsx
+++ b/apps/web/app/(app)/onboarding/welcome/page.tsx
@@ -1,5 +1,6 @@
"use client"
+import { useRef } from "react"
import { motion, AnimatePresence } from "motion/react"
import { cn } from "@lib/utils"
@@ -102,66 +103,70 @@ export default function WelcomePage() {
} = useWelcomeContext()
const { refetchOrganizations, setActiveOrg } = useAuth()
+ const submitLockRef = useRef(false)
const handleSubmit = async () => {
- localStorage.setItem("username", name)
- if (name.trim()) {
- setIsSubmitting(true)
+ const trimmed = name.trim()
+ if (!trimmed) return
+ if (submitLockRef.current) return
+ submitLockRef.current = true
+ localStorage.setItem("username", trimmed)
+ setIsSubmitting(true)
- try {
- await authClient.updateUser({
- displayUsername: name.trim(),
- username: generateUsername(name.trim()),
+ try {
+ await authClient.updateUser({
+ displayUsername: trimmed,
+ username: generateUsername(trimmed),
+ })
+
+ const refetchResult = await refetchOrganizations()
+ const refetchData = (
+ refetchResult as { data?: unknown[] | null | undefined }
+ )?.data
+ const existingOrgs = Array.isArray(refetchData) ? refetchData : []
+
+ if (existingOrgs.length > 0) {
+ analytics.onboardingNameSubmitted({
+ name_length: trimmed.length,
})
+ goToStep("greeting")
+ return
+ }
- const refetchResult = await refetchOrganizations()
- const refetchData = (
- refetchResult as { data?: unknown[] | null | undefined }
- )?.data
- const existingOrgs = Array.isArray(refetchData) ? refetchData : []
-
- if (existingOrgs.length > 0) {
- analytics.onboardingNameSubmitted({
- name_length: name.trim().length,
- })
- goToStep("greeting")
- return
- }
-
- const uniqueSlug = generateOrgSlug(name.trim())
- const completedAt = new Date().toISOString()
- const newOrg = await authClient.organization.create({
- name: name.trim(),
- slug: uniqueSlug,
- metadata: {
- signupSource: "consumer",
- webOnboarding: {
- completedAt: null,
- steps: {
- welcomeInput: {
- startedAt: completedAt,
- completedAt,
- data: {},
- },
+ const uniqueSlug = generateOrgSlug(trimmed)
+ const completedAt = new Date().toISOString()
+ const newOrg = await authClient.organization.create({
+ name: trimmed,
+ slug: uniqueSlug,
+ metadata: {
+ signupSource: "consumer",
+ webOnboarding: {
+ completedAt: null,
+ steps: {
+ welcomeInput: {
+ startedAt: completedAt,
+ completedAt,
+ data: {},
},
},
},
- })
+ },
+ })
- await setActiveOrg(newOrg.slug)
+ await setActiveOrg(newOrg.slug)
- analytics.onboardingNameSubmitted({ name_length: name.trim().length })
- goToStep("greeting")
- } catch (error) {
- console.error("Onboarding submit failed:", error)
- toast.error(
- error instanceof Error
- ? error.message
- : "Could not set up your workspace. Please try again.",
- )
- } finally {
- setIsSubmitting(false)
- }
+ analytics.onboardingNameSubmitted({ name_length: trimmed.length })
+ goToStep("greeting")
+ } catch (error) {
+ console.error("Onboarding submit failed:", error)
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : "Could not set up your workspace. Please try again.",
+ )
+ } finally {
+ submitLockRef.current = false
+ setIsSubmitting(false)
}
}
@@ -212,6 +217,7 @@ export default function WelcomePage() {
showUserSupermemory={
currentStep === "features" || currentStep === "memories"
}
+ showSkipOnboarding={currentStep !== "input"}
name={name}
/>
diff --git a/apps/web/components/initial-header.tsx b/apps/web/components/initial-header.tsx
index 91edfa57..5d8aebc9 100644
--- a/apps/web/components/initial-header.tsx
+++ b/apps/web/components/initial-header.tsx
@@ -1,14 +1,30 @@
+"use client"
+
import { Logo } from "@ui/assets/Logo"
import { Button } from "@ui/components/button"
+import { useRouter } from "next/navigation"
+import { useOrgOnboarding } from "@hooks/use-org-onboarding"
+import { analytics } from "@/lib/analytics"
export function InitialHeader({
showUserSupermemory,
+ showSkipOnboarding,
name,
}: {
showUserSupermemory?: boolean
+ showSkipOnboarding?: boolean
name?: string
}) {
+ const router = useRouter()
+ const { markOrgOnboarded, isLoading } = useOrgOnboarding()
const userName = name ? `${name.split(" ")[0]}'s` : "My"
+
+ const handleSkip = () => {
+ markOrgOnboarded()
+ analytics.onboardingCompleted()
+ router.push("/")
+ }
+
return (
@@ -24,13 +40,24 @@ export function InitialHeader({
)}
-
+
+ {showSkipOnboarding && !isLoading && (
+
+ )}
+
+
)
}
diff --git a/apps/web/components/onboarding/setup/header.tsx b/apps/web/components/onboarding/setup/header.tsx
index 59dd4fbb..97c0cea7 100644
--- a/apps/web/components/onboarding/setup/header.tsx
+++ b/apps/web/components/onboarding/setup/header.tsx
@@ -8,11 +8,20 @@ import { useRouter } from "next/navigation"
import { cn } from "@lib/utils"
import { dmSansClassName } from "@/lib/fonts"
import { useLocalStorageUsername } from "@hooks/use-local-storage-username"
+import { useOrgOnboarding } from "@hooks/use-org-onboarding"
+import { analytics } from "@/lib/analytics"
export function SetupHeader() {
const { user } = useAuth()
const router = useRouter()
const localStorageUsername = useLocalStorageUsername()
+ const { markOrgOnboarded, isLoading: isOrgLoading } = useOrgOnboarding()
+
+ const handleSkip = () => {
+ markOrgOnboarded()
+ analytics.onboardingCompleted()
+ router.push("/")
+ }
const displayName =
user?.displayUsername || localStorageUsername || user?.name || ""
@@ -59,9 +68,21 @@ export function SetupHeader() {
Setup
- {user && (
-
- )}
+
+ {!isOrgLoading && (
+
+ )}
+ {user && }
+
)
}
diff --git a/apps/web/components/onboarding/welcome/input-step.tsx b/apps/web/components/onboarding/welcome/input-step.tsx
index 077d4944..7cb7fdb3 100644
--- a/apps/web/components/onboarding/welcome/input-step.tsx
+++ b/apps/web/components/onboarding/welcome/input-step.tsx
@@ -1,4 +1,5 @@
import { motion } from "motion/react"
+import { cn } from "@lib/utils"
import { LabeledInput } from "@ui/input/labeled-input"
import { Button } from "@ui/components/button"
@@ -17,7 +18,10 @@ export function InputStep({
}: InputStepProps) {
return (
{
- if (e.key === "Enter") {
- handleSubmit()
- }
+ if (e.key !== "Enter") return
+ e.preventDefault()
+ if (isSubmitting) return
+ handleSubmit()
},
className: "!text-white placeholder:!text-[#525966] !h-[40px] pl-4",
}}
- onChange={(e) => setName((e.target as HTMLInputElement).value)}
+ onChange={(e) => {
+ if (isSubmitting) return
+ setName((e.target as HTMLInputElement).value)
+ }}
style={{
background:
"linear-gradient(0deg, rgba(91, 126, 245, 0.04) 0%, rgba(91, 126, 245, 0.04) 100%)",
}}
/>