2025-02-12 01:27:02 +00:00
|
|
|
"use client"
|
|
|
|
|
|
|
|
|
|
import { checkIfOrgDomainExists } from "../../../actions"
|
|
|
|
|
import { Button } from "@/components/ui/button"
|
|
|
|
|
import { Input } from "@/components/ui/input"
|
2025-02-14 00:20:01 +00:00
|
|
|
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form"
|
2025-02-12 01:27:02 +00:00
|
|
|
import { isServiceError } from "@/lib/utils"
|
|
|
|
|
import { useForm } from "react-hook-form"
|
|
|
|
|
import { z } from "zod"
|
|
|
|
|
import { zodResolver } from "@hookform/resolvers/zod"
|
|
|
|
|
import logoDark from "@/public/sb_logo_dark_large.png";
|
|
|
|
|
import logoLight from "@/public/sb_logo_light_large.png";
|
|
|
|
|
import Image from "next/image";
|
|
|
|
|
import { useState } from "react";
|
|
|
|
|
|
|
|
|
|
const onboardingFormSchema = z.object({
|
|
|
|
|
name: z.string()
|
|
|
|
|
.min(2, { message: "Organization name must be at least 3 characters long." })
|
|
|
|
|
.max(30, { message: "Organization name must be at most 30 characters long." }),
|
|
|
|
|
domain: z.string()
|
|
|
|
|
.min(2, { message: "Organization domain must be at least 3 characters long." })
|
2025-02-12 02:40:42 +00:00
|
|
|
.max(20, { message: "Organization domain must be at most 20 characters long." })
|
2025-02-13 19:41:32 +00:00
|
|
|
.regex(/^[a-z][a-z-]*[a-z]$/, {
|
|
|
|
|
message: "Domain must start and end with a letter, and can only contain lowercase letters and dashes.",
|
2025-02-13 19:32:17 +00:00
|
|
|
}),
|
2025-02-12 01:27:02 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export type OnboardingFormValues = z.infer<typeof onboardingFormSchema>
|
|
|
|
|
|
|
|
|
|
const defaultValues: Partial<OnboardingFormValues> = {
|
|
|
|
|
name: "",
|
|
|
|
|
domain: "",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface OrgCreateFormProps {
|
|
|
|
|
setOrgCreateData: (data: OnboardingFormValues) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function OrgCreateForm({ setOrgCreateData }: OrgCreateFormProps) {
|
|
|
|
|
const form = useForm<OnboardingFormValues>({ resolver: zodResolver(onboardingFormSchema), defaultValues })
|
|
|
|
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
|
|
|
|
|
|
|
|
async function submitOrgInfoForm(data: OnboardingFormValues) {
|
|
|
|
|
const res = await checkIfOrgDomainExists(data.domain);
|
2025-02-12 22:55:35 +00:00
|
|
|
if (isServiceError(res)) {
|
|
|
|
|
setErrorMessage("An error occurred while checking the domain. Please try clearing your cookies and trying again.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 01:27:02 +00:00
|
|
|
if (res) {
|
|
|
|
|
setErrorMessage("Organization domain already exists. Please try a different one.");
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
setOrgCreateData(data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 19:32:17 +00:00
|
|
|
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
|
|
const name = e.target.value
|
|
|
|
|
const domain = name.toLowerCase().replace(/\s+/g, "-")
|
|
|
|
|
form.setValue("domain", domain)
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 01:27:02 +00:00
|
|
|
return (
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<div className="flex justify-center">
|
|
|
|
|
<Image
|
|
|
|
|
src={logoDark}
|
|
|
|
|
className="h-16 w-auto hidden dark:block"
|
|
|
|
|
alt={"Sourcebot logo"}
|
|
|
|
|
priority={true}
|
|
|
|
|
/>
|
|
|
|
|
<Image
|
|
|
|
|
src={logoLight}
|
|
|
|
|
className="h-16 w-auto block dark:hidden"
|
|
|
|
|
alt={"Sourcebot logo"}
|
|
|
|
|
priority={true}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2025-02-14 00:20:01 +00:00
|
|
|
<h1 className="text-2xl font-bold">Let's create your organization</h1>
|
2025-02-12 01:27:02 +00:00
|
|
|
<Form {...form}>
|
|
|
|
|
<form onSubmit={form.handleSubmit(submitOrgInfoForm)} className="space-y-8">
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="name"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem>
|
|
|
|
|
<FormLabel>Organization Name</FormLabel>
|
|
|
|
|
<FormControl>
|
2025-02-13 19:32:17 +00:00
|
|
|
<Input
|
|
|
|
|
placeholder="Aperture Labs"
|
|
|
|
|
{...field}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
field.onChange(e)
|
|
|
|
|
handleNameChange(e)
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2025-02-12 01:27:02 +00:00
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
<FormField
|
|
|
|
|
control={form.control}
|
|
|
|
|
name="domain"
|
|
|
|
|
render={({ field }) => (
|
|
|
|
|
<FormItem>
|
|
|
|
|
<FormLabel>Organization Domain</FormLabel>
|
|
|
|
|
<FormControl>
|
|
|
|
|
<div className="flex items-center">
|
2025-02-13 19:32:17 +00:00
|
|
|
<Input placeholder="aperature-labs" {...field} className="w-1/2" />
|
2025-02-12 01:27:02 +00:00
|
|
|
<span className="ml-2">.sourcebot.dev</span>
|
|
|
|
|
</div>
|
|
|
|
|
</FormControl>
|
|
|
|
|
<FormMessage />
|
|
|
|
|
</FormItem>
|
|
|
|
|
)}
|
|
|
|
|
/>
|
|
|
|
|
{errorMessage && <p className="text-red-500">{errorMessage}</p>}
|
|
|
|
|
<div className="flex justify-center">
|
|
|
|
|
<Button type="submit">Create</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
|
|
|
|
</Form>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|