fix: input disable when onboarding org creation (#855)

This commit is contained in:
MaheshtheDev 2026-04-16 16:40:15 +00:00
parent 76b0f2746b
commit b8e5376b99
5 changed files with 136 additions and 66 deletions

View file

@ -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)

View file

@ -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}
/>

View file

@ -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 (
<div className="flex p-6 justify-between items-center">
<div className="flex items-center z-10!">
@ -24,13 +40,24 @@ export function InitialHeader({
</div>
)}
</div>
<Button
variant="newDefault"
className="rounded-2xl text-base gap-1 h-11! z-10!"
size={"lg"}
>
Memory API <span className="text-xs mt-[4px]"></span>
</Button>
<div className="flex items-center gap-3 z-10!">
{showSkipOnboarding && !isLoading && (
<button
type="button"
onClick={handleSkip}
className="text-sm text-white/40 hover:text-white/70 transition-colors cursor-pointer"
>
Skip Onboarding
</button>
)}
<Button
variant="newDefault"
className="rounded-2xl text-base gap-1 h-11!"
size="lg"
>
Memory API <span className="text-xs mt-[4px]"></span>
</Button>
</div>
</div>
)
}

View file

@ -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() {
</span>
<span className="text-white/50 font-medium shrink-0">Setup</span>
</nav>
{user && (
<UserProfileMenu className="z-10" avatarClassName="border-border" />
)}
<div className="flex items-center gap-3 z-10">
{!isOrgLoading && (
<button
type="button"
onClick={handleSkip}
className={cn(
"text-sm text-white/40 hover:text-white/70 transition-colors cursor-pointer",
dmSansClassName(),
)}
>
Skip Onboarding
</button>
)}
{user && <UserProfileMenu avatarClassName="border-border" />}
</div>
</motion.div>
)
}

View file

@ -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 (
<motion.div
className="text-center min-w-[250px] flex flex-col"
className={cn(
"text-center min-w-[250px] flex flex-col",
isSubmitting && "pointer-events-none",
)}
style={{ gap: "24px" }}
initial={{
opacity: 0,
@ -53,20 +57,27 @@ export function InputStep({
className="w-full flex-1"
inputProps={{
defaultValue: name,
disabled: isSubmitting,
onKeyDown: (e) => {
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%)",
}}
/>
<Button
type="button"
disabled={isSubmitting}
className={`rounded-[8px] w-8 h-8 p-2 absolute right-1 border-[0.5px] border-[#161F2C] hover:cursor-pointer hover:scale-[0.95] active:scale-[0.95] transition-transform ${
isSubmitting ? "scale-[0.90]" : ""
}`}