mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-04 22:02:16 +02:00
Add CreateSearchSpaceDialog component
This commit is contained in:
parent
eed04e9b27
commit
d5580fe3ac
6 changed files with 198 additions and 0 deletions
|
|
@ -12,6 +12,7 @@ export type {
|
||||||
} from "./types/layout.types";
|
} from "./types/layout.types";
|
||||||
export {
|
export {
|
||||||
ChatListItem,
|
ChatListItem,
|
||||||
|
CreateSearchSpaceDialog,
|
||||||
Header,
|
Header,
|
||||||
IconRail,
|
IconRail,
|
||||||
LayoutShell,
|
LayoutShell,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useAtomValue } from "jotai";
|
||||||
|
import { Loader2, Plus, Search } from "lucide-react";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import * as z from "zod";
|
||||||
|
import { createSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from "@/components/ui/form";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { trackSearchSpaceCreated } from "@/lib/posthog/events";
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
name: z.string().min(1, "Name is required"),
|
||||||
|
description: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
interface CreateSearchSpaceDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CreateSearchSpaceDialog({ open, onOpenChange }: CreateSearchSpaceDialogProps) {
|
||||||
|
const t = useTranslations("searchSpace");
|
||||||
|
const tCommon = useTranslations("common");
|
||||||
|
const router = useRouter();
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const { mutateAsync: createSearchSpace } = useAtomValue(createSearchSpaceMutationAtom);
|
||||||
|
|
||||||
|
const form = useForm<FormValues>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
defaultValues: {
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSubmit = async (values: FormValues) => {
|
||||||
|
setIsSubmitting(true);
|
||||||
|
try {
|
||||||
|
const result = await createSearchSpace({
|
||||||
|
name: values.name,
|
||||||
|
description: values.description || "",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Track search space creation
|
||||||
|
trackSearchSpaceCreated(result.id, values.name);
|
||||||
|
|
||||||
|
// Reset form and close dialog
|
||||||
|
form.reset();
|
||||||
|
onOpenChange(false);
|
||||||
|
|
||||||
|
// Redirect to the newly created search space's onboarding
|
||||||
|
router.push(`/dashboard/${result.id}/onboard`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to create search space:", error);
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenChange = (newOpen: boolean) => {
|
||||||
|
if (!newOpen) {
|
||||||
|
form.reset();
|
||||||
|
}
|
||||||
|
onOpenChange(newOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
|
||||||
|
<Search className="h-5 w-5 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<DialogTitle>{t("create_title")}</DialogTitle>
|
||||||
|
<DialogDescription>{t("create_description")}</DialogDescription>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<Form {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(handleSubmit)} className="flex flex-col gap-4">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t("name_label")}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t("name_placeholder")}
|
||||||
|
{...field}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="description"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>
|
||||||
|
{t("description_label")}{" "}
|
||||||
|
<span className="text-muted-foreground font-normal">
|
||||||
|
({tCommon("optional")})
|
||||||
|
</span>
|
||||||
|
</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder={t("description_placeholder")} {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DialogFooter className="flex gap-2 pt-2">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => handleOpenChange(false)}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
>
|
||||||
|
{tCommon("cancel")}
|
||||||
|
</Button>
|
||||||
|
<Button type="submit" disabled={isSubmitting}>
|
||||||
|
{isSubmitting ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
{t("creating")}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
{t("create_button")}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
2
surfsense_web/components/layout/ui/dialogs/index.ts
Normal file
2
surfsense_web/components/layout/ui/dialogs/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { CreateSearchSpaceDialog } from "./CreateSearchSpaceDialog";
|
||||||
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
export { CreateSearchSpaceDialog } from "./dialogs";
|
||||||
export { Header } from "./header";
|
export { Header } from "./header";
|
||||||
export { IconRail, NavIcon, SearchSpaceAvatar } from "./icon-rail";
|
export { IconRail, NavIcon, SearchSpaceAvatar } from "./icon-rail";
|
||||||
export { LayoutShell } from "./shell";
|
export { LayoutShell } from "./shell";
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,16 @@
|
||||||
"creating_account_btn": "Creating account...",
|
"creating_account_btn": "Creating account...",
|
||||||
"redirecting_login": "Redirecting to login page..."
|
"redirecting_login": "Redirecting to login page..."
|
||||||
},
|
},
|
||||||
|
"searchSpace": {
|
||||||
|
"create_title": "Create Search Space",
|
||||||
|
"create_description": "Create a new search space to organize your knowledge",
|
||||||
|
"name_label": "Name",
|
||||||
|
"name_placeholder": "Enter search space name",
|
||||||
|
"description_label": "Description",
|
||||||
|
"description_placeholder": "What is this search space for?",
|
||||||
|
"create_button": "Create",
|
||||||
|
"creating": "Creating..."
|
||||||
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"title": "Dashboard",
|
"title": "Dashboard",
|
||||||
"search_spaces": "Search Spaces",
|
"search_spaces": "Search Spaces",
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,16 @@
|
||||||
"creating_account_btn": "创建中...",
|
"creating_account_btn": "创建中...",
|
||||||
"redirecting_login": "正在跳转到登录页面..."
|
"redirecting_login": "正在跳转到登录页面..."
|
||||||
},
|
},
|
||||||
|
"searchSpace": {
|
||||||
|
"create_title": "创建搜索空间",
|
||||||
|
"create_description": "创建一个新的搜索空间来组织您的知识",
|
||||||
|
"name_label": "名称",
|
||||||
|
"name_placeholder": "输入搜索空间名称",
|
||||||
|
"description_label": "描述",
|
||||||
|
"description_placeholder": "这个搜索空间是做什么的?",
|
||||||
|
"create_button": "创建",
|
||||||
|
"creating": "创建中..."
|
||||||
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"title": "仪表盘",
|
"title": "仪表盘",
|
||||||
"search_spaces": "搜索空间",
|
"search_spaces": "搜索空间",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue