mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-08 22:22:17 +02:00
Merge remote-tracking branch 'upstream/ci_mvp' into feat/ci-ui-changes
This commit is contained in:
commit
fd8d1273cd
188 changed files with 2163 additions and 3644 deletions
|
|
@ -156,7 +156,7 @@ export function AutomationBuilderForm({
|
|||
if (mode === "edit" && automation) {
|
||||
return { ...buildUpdatePayload(formForPayload), status: automation.status };
|
||||
}
|
||||
const { search_space_id: _ignored, ...rest } = buildCreatePayload(formForPayload, workspaceId);
|
||||
const { workspace_id: _ignored, ...rest } = buildCreatePayload(formForPayload, workspaceId);
|
||||
return rest;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export function AutomationModelFields({
|
|||
errors,
|
||||
}: AutomationModelFieldsProps) {
|
||||
const { llm, image, vision, isLoading } = useAutomationEligibleModels();
|
||||
const rolesHref = `/dashboard/${workspaceId}/search-space-settings/models`;
|
||||
const rolesHref = `/dashboard/${workspaceId}/workspace-settings/models`;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
modelConnectionsAtom,
|
||||
modelRolesAtom,
|
||||
} from "@/atoms/model-connections/model-connections-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { DocumentUploadDialogProvider } from "@/components/assistant-ui/document-upload-popup";
|
||||
import { LayoutDataProvider } from "@/components/layout";
|
||||
import { OnboardingTour } from "@/components/onboarding-tour";
|
||||
|
|
|
|||
|
|
@ -754,8 +754,8 @@ export default function NewChatPage() {
|
|||
syncChatTab({
|
||||
chatId: thread.id,
|
||||
title: thread.title,
|
||||
chatUrl: `/dashboard/${thread.search_space_id ?? workspaceId}/new-chat/${thread.id}`,
|
||||
searchSpaceId: thread.search_space_id ?? workspaceId,
|
||||
chatUrl: `/dashboard/${thread.workspace_id ?? workspaceId}/new-chat/${thread.id}`,
|
||||
searchSpaceId: thread.workspace_id ?? workspaceId,
|
||||
visibility: thread.visibility,
|
||||
hasComments: thread.has_comments ?? false,
|
||||
});
|
||||
|
|
@ -906,7 +906,7 @@ export default function NewChatPage() {
|
|||
|
||||
setCurrentThreadMetadata({
|
||||
id: currentThread.id,
|
||||
searchSpaceId: currentThread.search_space_id ?? workspaceId,
|
||||
searchSpaceId: currentThread.workspace_id ?? workspaceId,
|
||||
visibility,
|
||||
hasComments: currentThread.has_comments ?? false,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ export function TeamContent({ workspaceId }: TeamContentProps) {
|
|||
const handleRevokeInvite = useCallback(
|
||||
async (inviteId: number): Promise<boolean> => {
|
||||
const request: DeleteInviteRequest = {
|
||||
search_space_id: workspaceId,
|
||||
workspace_id: workspaceId,
|
||||
invite_id: inviteId,
|
||||
};
|
||||
await revokeInvite(request);
|
||||
|
|
@ -151,7 +151,7 @@ export function TeamContent({ workspaceId }: TeamContentProps) {
|
|||
const handleCreateInvite = useCallback(
|
||||
async (inviteData: CreateInviteRequest["data"]) => {
|
||||
const request: CreateInviteRequest = {
|
||||
search_space_id: workspaceId,
|
||||
workspace_id: workspaceId,
|
||||
data: inviteData,
|
||||
};
|
||||
return await createInvite(request);
|
||||
|
|
@ -162,7 +162,7 @@ export function TeamContent({ workspaceId }: TeamContentProps) {
|
|||
const handleUpdateMember = useCallback(
|
||||
async (membershipId: number, roleId: number | null): Promise<Membership> => {
|
||||
const request: UpdateMembershipRequest = {
|
||||
search_space_id: workspaceId,
|
||||
workspace_id: workspaceId,
|
||||
membership_id: membershipId,
|
||||
data: { role_id: roleId },
|
||||
};
|
||||
|
|
@ -174,7 +174,7 @@ export function TeamContent({ workspaceId }: TeamContentProps) {
|
|||
const handleRemoveMember = useCallback(
|
||||
async (membershipId: number) => {
|
||||
const request: DeleteMembershipRequest = {
|
||||
search_space_id: workspaceId,
|
||||
workspace_id: workspaceId,
|
||||
membership_id: membershipId,
|
||||
};
|
||||
await deleteMember(request);
|
||||
|
|
@ -185,13 +185,13 @@ export function TeamContent({ workspaceId }: TeamContentProps) {
|
|||
|
||||
const { data: roles = [], isLoading: rolesLoading } = useQuery({
|
||||
queryKey: cacheKeys.roles.all(workspaceId.toString()),
|
||||
queryFn: () => rolesApiService.getRoles({ search_space_id: workspaceId }),
|
||||
queryFn: () => rolesApiService.getRoles({ workspace_id: workspaceId }),
|
||||
enabled: !!workspaceId,
|
||||
});
|
||||
|
||||
const { data: invites = [], isLoading: invitesLoading } = useQuery({
|
||||
queryKey: cacheKeys.invites.all(workspaceId.toString()),
|
||||
queryFn: () => invitesApiService.getInvites({ search_space_id: workspaceId }),
|
||||
queryFn: () => invitesApiService.getInvites({ workspace_id: workspaceId }),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
});
|
||||
|
||||
|
|
@ -581,7 +581,7 @@ function MemberRow({
|
|||
<AlertDialogTitle>Remove member?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
This will remove <span className="font-medium">{member.user_email}</span>{" "}
|
||||
from this search space. They will lose access to all resources.
|
||||
from this workspace. They will lose access to all resources.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
|
|
@ -599,7 +599,7 @@ function MemberRow({
|
|||
<DropdownMenuSeparator className="bg-popover-border" />
|
||||
<DropdownMenuItem
|
||||
onClick={() =>
|
||||
router.push(`/dashboard/${workspaceId}/search-space-settings/team-roles`)
|
||||
router.push(`/dashboard/${workspaceId}/workspace-settings/team-roles`)
|
||||
}
|
||||
>
|
||||
Manage Roles
|
||||
|
|
@ -709,7 +709,7 @@ function CreateInviteDialog({
|
|||
Invite Created!
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Share this link to invite people to your search space.
|
||||
Share this link to invite people to your workspace.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3 py-2 md:py-4">
|
||||
|
|
@ -748,7 +748,7 @@ function CreateInviteDialog({
|
|||
<DialogHeader>
|
||||
<DialogTitle>Invite Members</DialogTitle>
|
||||
<DialogDescription>
|
||||
Create a link to invite people to this search space.
|
||||
Create a link to invite people to this workspace.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3 py-2 md:py-4">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { AlertTriangle, Info, ShieldCheck, Trash2 } from "lucide-react";
|
|||
import { useCallback, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { agentFlagsAtom } from "@/atoms/agent/agent-flags-query.atom";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import {
|
||||
AlertDialog,
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import {
|
|||
import { Separator } from "@/components/ui/separator";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import type { SearchSpace } from "@/contracts/types/search-space.types";
|
||||
import type { SearchSpace } from "@/contracts/types/workspace.types";
|
||||
import { useElectronAPI } from "@/hooks/use-platform";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
|
||||
|
||||
export function DesktopContent() {
|
||||
const api = useElectronAPI();
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ import {
|
|||
} from "@/components/ui/select";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import type { SearchSpace } from "@/contracts/types/search-space.types";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import type { SearchSpace } from "@/contracts/types/workspace.types";
|
||||
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-fetch";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
|
|
|||
|
|
@ -11,7 +11,5 @@ export default function UserSettingsLayout({
|
|||
}) {
|
||||
const { workspace_id } = use(params);
|
||||
|
||||
return (
|
||||
<UserSettingsLayoutShell workspaceId={workspace_id}>{children}</UserSettingsLayoutShell>
|
||||
);
|
||||
return <UserSettingsLayoutShell workspaceId={workspace_id}>{children}</UserSettingsLayoutShell>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ export function SearchSpaceSettingsLayoutShell({
|
|||
const selectedLabel = navItems.find((item) => item.value === activeTab)?.label ?? t("title");
|
||||
|
||||
const hrefFor = (tab: SearchSpaceSettingsTab) =>
|
||||
`/dashboard/${workspaceId}/search-space-settings/${tab}`;
|
||||
`/dashboard/${workspaceId}/workspace-settings/${tab}`;
|
||||
|
||||
return (
|
||||
<section className="flex h-full min-h-[min(680px,calc(100vh-5rem))] w-full select-none flex-col gap-6 md:pt-6 md:flex-row">
|
||||
|
|
@ -6,5 +6,5 @@ export default async function SearchSpaceSettingsPage({
|
|||
params: Promise<{ workspace_id: string }>;
|
||||
}) {
|
||||
const { workspace_id } = await params;
|
||||
redirect(`/dashboard/${workspace_id}/search-space-settings/general`);
|
||||
redirect(`/dashboard/${workspace_id}/workspace-settings/general`);
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import { motion } from "motion/react";
|
|||
import { useRouter } from "next/navigation";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useEffect, useState } from "react";
|
||||
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { searchSpacesAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { CreateSearchSpaceDialog } from "@/components/layout";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { Separator } from "@/components/ui/separator";
|
|||
import { ShortcutKbd } from "@/components/ui/shortcut-kbd";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { useElectronAPI } from "@/hooks/use-platform";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
|
||||
import { getPostLoginRedirectPath } from "@/lib/auth-utils";
|
||||
|
||||
type ShortcutKey = "generalAssist" | "quickAsk" | "screenshotAssist";
|
||||
|
|
|
|||
|
|
@ -98,15 +98,11 @@ export default function InviteAcceptPage() {
|
|||
|
||||
// Track invite accepted and user added events
|
||||
trackSearchSpaceInviteAccepted(
|
||||
result.search_space_id,
|
||||
result.search_space_name,
|
||||
result.role_name
|
||||
);
|
||||
trackSearchSpaceUserAdded(
|
||||
result.search_space_id,
|
||||
result.search_space_name,
|
||||
result.workspace_id,
|
||||
result.workspace_name,
|
||||
result.role_name
|
||||
);
|
||||
trackSearchSpaceUserAdded(result.workspace_id, result.workspace_name, result.role_name);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to accept invite");
|
||||
|
|
@ -117,7 +113,7 @@ export default function InviteAcceptPage() {
|
|||
|
||||
const handleDecline = () => {
|
||||
// Track invite declined event
|
||||
trackSearchSpaceInviteDeclined(inviteInfo?.search_space_name);
|
||||
trackSearchSpaceInviteDeclined(inviteInfo?.workspace_name);
|
||||
router.push("/dashboard");
|
||||
};
|
||||
|
||||
|
|
@ -180,7 +176,7 @@ export default function InviteAcceptPage() {
|
|||
</motion.div>
|
||||
<CardTitle className="text-2xl">Welcome to the team!</CardTitle>
|
||||
<CardDescription>
|
||||
You've successfully joined {acceptedData.search_space_name}
|
||||
You've successfully joined {acceptedData.workspace_name}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
|
|
@ -190,7 +186,7 @@ export default function InviteAcceptPage() {
|
|||
<Users className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium">{acceptedData.search_space_name}</p>
|
||||
<p className="font-medium">{acceptedData.workspace_name}</p>
|
||||
<p className="text-sm text-muted-foreground">Search Space</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -208,7 +204,7 @@ export default function InviteAcceptPage() {
|
|||
<CardFooter>
|
||||
<Button
|
||||
className="w-full gap-2"
|
||||
onClick={() => router.push(`/dashboard/${acceptedData.search_space_id}`)}
|
||||
onClick={() => router.push(`/dashboard/${acceptedData.workspace_id}`)}
|
||||
>
|
||||
Go to Search Space
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
|
|
@ -260,7 +256,7 @@ export default function InviteAcceptPage() {
|
|||
</motion.div>
|
||||
<CardTitle className="text-2xl">You're Invited!</CardTitle>
|
||||
<CardDescription>
|
||||
Sign in to join {inviteInfo?.search_space_name || "this search space"}
|
||||
Sign in to join {inviteInfo?.workspace_name || "this search space"}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
|
|
@ -270,7 +266,7 @@ export default function InviteAcceptPage() {
|
|||
<Users className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium">{inviteInfo?.search_space_name}</p>
|
||||
<p className="font-medium">{inviteInfo?.workspace_name}</p>
|
||||
<p className="text-sm text-muted-foreground">Search Space</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -307,7 +303,7 @@ export default function InviteAcceptPage() {
|
|||
</motion.div>
|
||||
<CardTitle className="text-2xl">You're Invited!</CardTitle>
|
||||
<CardDescription>
|
||||
Accept this invite to join {inviteInfo?.search_space_name || "this search space"}
|
||||
Accept this invite to join {inviteInfo?.workspace_name || "this search space"}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
|
|
@ -317,7 +313,7 @@ export default function InviteAcceptPage() {
|
|||
<Users className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium">{inviteInfo?.search_space_name}</p>
|
||||
<p className="font-medium">{inviteInfo?.workspace_name}</p>
|
||||
<p className="text-sm text-muted-foreground">Search Space</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { atom } from "jotai";
|
|||
import { atomWithQuery } from "jotai-tanstack-query";
|
||||
import { agentToolsApiService } from "@/lib/apis/agent-tools-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "../workspaces/workspace-query.atoms";
|
||||
|
||||
export const agentToolsAtom = atomWithQuery((_get) => ({
|
||||
queryKey: cacheKeys.agentTools.all(),
|
||||
|
|
@ -42,7 +42,7 @@ const hydratedForAtom = atom<string | null>(null);
|
|||
*/
|
||||
export const disabledToolsAtom = atom(
|
||||
(get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
const hydratedFor = get(hydratedForAtom);
|
||||
if (searchSpaceId && hydratedFor !== searchSpaceId) {
|
||||
return loadDisabledTools(searchSpaceId);
|
||||
|
|
@ -50,7 +50,7 @@ export const disabledToolsAtom = atom(
|
|||
return get(disabledToolsBaseAtom);
|
||||
},
|
||||
(get, set, update: string[] | ((prev: string[]) => string[])) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
const prev = get(disabledToolsBaseAtom);
|
||||
const next = typeof update === "function" ? update(prev) : update;
|
||||
set(disabledToolsBaseAtom, next);
|
||||
|
|
@ -66,7 +66,7 @@ export const disabledToolsAtom = atom(
|
|||
* Call this from a useEffect in a component that has access to the search space.
|
||||
*/
|
||||
export const hydrateDisabledToolsAtom = atom(null, (get, set) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
if (!searchSpaceId) return;
|
||||
const stored = loadDisabledTools(searchSpaceId);
|
||||
set(disabledToolsBaseAtom, stored);
|
||||
|
|
@ -75,7 +75,7 @@ export const hydrateDisabledToolsAtom = atom(null, (get, set) => {
|
|||
|
||||
/** Toggle a single tool's enabled/disabled state */
|
||||
export const toggleToolAtom = atom(null, (get, set, toolName: string) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
const current = get(disabledToolsBaseAtom);
|
||||
const next = current.includes(toolName)
|
||||
? current.filter((t) => t !== toolName)
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ export const createAutomationMutationAtom = atomWithMutation(() => ({
|
|||
return automationsApiService.createAutomation(request);
|
||||
},
|
||||
onSuccess: (automation, variables) => {
|
||||
invalidateList(variables.search_space_id);
|
||||
invalidateList(variables.workspace_id);
|
||||
toast.success("Automation created");
|
||||
trackAutomationCreated({
|
||||
search_space_id: variables.search_space_id,
|
||||
workspace_id: variables.workspace_id,
|
||||
automation_id: automation.id,
|
||||
task_count: variables.definition.plan.length,
|
||||
trigger_type: variables.triggers?.[0]?.type ?? "none",
|
||||
|
|
@ -67,7 +67,7 @@ export const createAutomationMutationAtom = atomWithMutation(() => ({
|
|||
console.error("Error creating automation:", error);
|
||||
toast.error("Failed to create automation");
|
||||
trackAutomationCreateFailed({
|
||||
search_space_id: variables.search_space_id,
|
||||
workspace_id: variables.workspace_id,
|
||||
error: error.message,
|
||||
});
|
||||
},
|
||||
|
|
@ -80,20 +80,20 @@ export const updateAutomationMutationAtom = atomWithMutation(() => ({
|
|||
},
|
||||
onSuccess: (automation, vars) => {
|
||||
invalidateDetail(vars.automationId);
|
||||
invalidateList(automation.search_space_id);
|
||||
invalidateList(automation.workspace_id);
|
||||
toast.success("Automation updated");
|
||||
// A status-only patch (pause/resume/archive) is a distinct action from a
|
||||
// definition/name edit, so split it into its own event.
|
||||
if (vars.patch.status && !vars.patch.definition) {
|
||||
trackAutomationStatusChanged({
|
||||
automation_id: vars.automationId,
|
||||
search_space_id: automation.search_space_id,
|
||||
workspace_id: automation.workspace_id,
|
||||
next_status: vars.patch.status,
|
||||
});
|
||||
} else {
|
||||
trackAutomationUpdated({
|
||||
automation_id: vars.automationId,
|
||||
search_space_id: automation.search_space_id,
|
||||
workspace_id: automation.workspace_id,
|
||||
has_definition_change: !!vars.patch.definition,
|
||||
has_name_change: vars.patch.name != null,
|
||||
has_description_change: vars.patch.description !== undefined,
|
||||
|
|
@ -123,7 +123,7 @@ export const deleteAutomationMutationAtom = atomWithMutation(() => ({
|
|||
toast.success("Automation deleted");
|
||||
trackAutomationDeleted({
|
||||
automation_id: vars.automationId,
|
||||
search_space_id: vars.searchSpaceId,
|
||||
workspace_id: vars.searchSpaceId,
|
||||
});
|
||||
},
|
||||
onError: (error: Error, vars) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { atomWithQuery } from "jotai-tanstack-query";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { automationsApiService } from "@/lib/apis/automations-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ const DEFAULT_LIMIT = 50;
|
|||
const DEFAULT_OFFSET = 0;
|
||||
|
||||
export const automationsListAtom = atomWithQuery((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
queryKey: cacheKeys.automations.list(Number(searchSpaceId ?? 0), DEFAULT_LIMIT, DEFAULT_OFFSET),
|
||||
|
|
@ -22,7 +22,7 @@ export const automationsListAtom = atomWithQuery((get) => {
|
|||
return { items: [], total: 0 };
|
||||
}
|
||||
return automationsApiService.listAutomations({
|
||||
search_space_id: Number(searchSpaceId),
|
||||
workspace_id: Number(searchSpaceId),
|
||||
limit: DEFAULT_LIMIT,
|
||||
offset: DEFAULT_OFFSET,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ import type {
|
|||
import { connectorsApiService } from "@/lib/apis/connectors-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { queryClient } from "@/lib/query-client/client";
|
||||
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "../workspaces/workspace-query.atoms";
|
||||
|
||||
export const createConnectorMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
mutationKey: cacheKeys.connectors.all(searchSpaceId ?? ""),
|
||||
|
|
@ -32,7 +32,7 @@ export const createConnectorMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const updateConnectorMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
mutationKey: cacheKeys.connectors.all(searchSpaceId ?? ""),
|
||||
|
|
@ -54,7 +54,7 @@ export const updateConnectorMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const deleteConnectorMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
mutationKey: cacheKeys.connectors.all(searchSpaceId ?? ""),
|
||||
|
|
@ -80,7 +80,7 @@ export const deleteConnectorMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const indexConnectorMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
mutationKey: cacheKeys.connectors.index(),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { atomWithQuery } from "jotai-tanstack-query";
|
||||
import { connectorsApiService } from "@/lib/apis/connectors-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "../workspaces/workspace-query.atoms";
|
||||
|
||||
export const connectorsAtom = atomWithQuery((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
queryKey: cacheKeys.connectors.all(searchSpaceId!),
|
||||
|
|
@ -13,7 +13,7 @@ export const connectorsAtom = atomWithQuery((get) => {
|
|||
queryFn: async () => {
|
||||
return connectorsApiService.getConnectors({
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId!,
|
||||
workspace_id: searchSpaceId!,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { atomWithMutation } from "jotai-tanstack-query";
|
||||
import { toast } from "sonner";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import type {
|
||||
CreateDocumentRequest,
|
||||
DeleteDocumentRequest,
|
||||
|
|
@ -14,7 +14,7 @@ import { queryClient } from "@/lib/query-client/client";
|
|||
import { globalDocumentsQueryParamsAtom } from "./ui.atoms";
|
||||
|
||||
export const createDocumentMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
const documentsQueryParams = get(globalDocumentsQueryParamsAtom);
|
||||
|
||||
return {
|
||||
|
|
@ -34,7 +34,7 @@ export const createDocumentMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const uploadDocumentMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
const documentsQueryParams = get(globalDocumentsQueryParamsAtom);
|
||||
|
||||
return {
|
||||
|
|
@ -54,7 +54,7 @@ export const uploadDocumentMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const updateDocumentMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
const documentsQueryParams = get(globalDocumentsQueryParamsAtom);
|
||||
|
||||
return {
|
||||
|
|
@ -77,7 +77,7 @@ export const updateDocumentMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const deleteDocumentMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
const documentsQueryParams = get(globalDocumentsQueryParamsAtom);
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export const createInviteMutationAtom = atomWithMutation(() => ({
|
|||
},
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: cacheKeys.invites.all(variables.search_space_id.toString()),
|
||||
queryKey: cacheKeys.invites.all(variables.workspace_id.toString()),
|
||||
});
|
||||
toast.success("Invite created successfully");
|
||||
},
|
||||
|
|
@ -40,7 +40,7 @@ export const updateInviteMutationAtom = atomWithMutation(() => ({
|
|||
},
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: cacheKeys.invites.all(variables.search_space_id.toString()),
|
||||
queryKey: cacheKeys.invites.all(variables.workspace_id.toString()),
|
||||
});
|
||||
toast.success("Invite updated successfully");
|
||||
},
|
||||
|
|
@ -60,7 +60,7 @@ export const deleteInviteMutationAtom = atomWithMutation(() => ({
|
|||
},
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: cacheKeys.invites.all(variables.search_space_id.toString()),
|
||||
queryKey: cacheKeys.invites.all(variables.workspace_id.toString()),
|
||||
});
|
||||
toast.success("Invite deleted successfully");
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { atomWithQuery } from "jotai-tanstack-query";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { invitesApiService } from "@/lib/apis/invites-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
|
||||
export const invitesAtom = atomWithQuery((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
queryKey: cacheKeys.invites.all(searchSpaceId?.toString() ?? ""),
|
||||
|
|
@ -15,7 +15,7 @@ export const invitesAtom = atomWithQuery((get) => {
|
|||
return [];
|
||||
}
|
||||
return invitesApiService.getInvites({
|
||||
search_space_id: Number(searchSpaceId),
|
||||
workspace_id: Number(searchSpaceId),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { atomWithMutation } from "jotai-tanstack-query";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import type {
|
||||
CreateLogRequest,
|
||||
DeleteLogRequest,
|
||||
|
|
@ -13,7 +13,7 @@ import { queryClient } from "@/lib/query-client/client";
|
|||
* Create Log Mutation
|
||||
*/
|
||||
export const createLogMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
return {
|
||||
mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined),
|
||||
enabled: !!searchSpaceId,
|
||||
|
|
@ -29,7 +29,7 @@ export const createLogMutationAtom = atomWithMutation((get) => {
|
|||
* Update Log Mutation
|
||||
*/
|
||||
export const updateLogMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
return {
|
||||
mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined),
|
||||
enabled: !!searchSpaceId,
|
||||
|
|
@ -45,7 +45,7 @@ export const updateLogMutationAtom = atomWithMutation((get) => {
|
|||
* Delete Log Mutation
|
||||
*/
|
||||
export const deleteLogMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
return {
|
||||
mutationKey: cacheKeys.logs.list(searchSpaceId ?? undefined),
|
||||
enabled: !!searchSpaceId,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export const updateMemberMutationAtom = atomWithMutation(() => {
|
|||
onSuccess: (_: UpdateMembershipResponse, request: UpdateMembershipRequest) => {
|
||||
toast.success("Member updated successfully");
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: cacheKeys.members.all(request.search_space_id.toString()),
|
||||
queryKey: cacheKeys.members.all(request.workspace_id.toString()),
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
|
|
@ -39,7 +39,7 @@ export const deleteMemberMutationAtom = atomWithMutation(() => {
|
|||
onSuccess: (_: DeleteMembershipResponse, request: DeleteMembershipRequest) => {
|
||||
toast.success("Member removed successfully");
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: cacheKeys.members.all(request.search_space_id.toString()),
|
||||
queryKey: cacheKeys.members.all(request.workspace_id.toString()),
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
|
|
@ -57,7 +57,7 @@ export const leaveSearchSpaceMutationAtom = atomWithMutation(() => {
|
|||
onSuccess: (_: LeaveSearchSpaceResponse, request: LeaveSearchSpaceRequest) => {
|
||||
toast.success("Successfully left the search space");
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: cacheKeys.members.all(request.search_space_id.toString()),
|
||||
queryKey: cacheKeys.members.all(request.workspace_id.toString()),
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { useAtomValue } from "jotai";
|
||||
import { atomWithQuery } from "jotai-tanstack-query";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { membersApiService } from "@/lib/apis/members-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
|
||||
export const membersAtom = atomWithQuery((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
queryKey: cacheKeys.members.all(searchSpaceId?.toString() ?? ""),
|
||||
|
|
@ -17,14 +17,14 @@ export const membersAtom = atomWithQuery((get) => {
|
|||
return [];
|
||||
}
|
||||
return membersApiService.getMembers({
|
||||
search_space_id: Number(searchSpaceId),
|
||||
workspace_id: Number(searchSpaceId),
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const myAccessAtom = atomWithQuery((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
queryKey: cacheKeys.members.myAccess(searchSpaceId?.toString() ?? ""),
|
||||
|
|
@ -35,7 +35,7 @@ export const myAccessAtom = atomWithQuery((get) => {
|
|||
return null;
|
||||
}
|
||||
return membersApiService.getMyAccess({
|
||||
search_space_id: Number(searchSpaceId),
|
||||
workspace_id: Number(searchSpaceId),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import type {
|
|||
import { modelConnectionsApiService } from "@/lib/apis/model-connections-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { queryClient } from "@/lib/query-client/client";
|
||||
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "../workspaces/workspace-query.atoms";
|
||||
|
||||
function invalidateModelConnections(searchSpaceId: number) {
|
||||
queryClient.invalidateQueries({
|
||||
|
|
@ -40,14 +40,14 @@ function upsertModelConnection(searchSpaceId: number, connection: ConnectionRead
|
|||
}
|
||||
|
||||
export const createModelConnectionMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
mutationKey: ["model-connections", "create"],
|
||||
mutationFn: (request: ConnectionCreateRequest) =>
|
||||
modelConnectionsApiService.createConnection(request),
|
||||
onSuccess: (connection: ConnectionRead, request: ConnectionCreateRequest) => {
|
||||
const resolvedSearchSpaceId = Number(
|
||||
request.search_space_id ?? connection.search_space_id ?? searchSpaceId
|
||||
request.workspace_id ?? connection.workspace_id ?? searchSpaceId
|
||||
);
|
||||
toast.success("Connection created");
|
||||
if (resolvedSearchSpaceId > 0) {
|
||||
|
|
@ -60,7 +60,7 @@ export const createModelConnectionMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const updateModelConnectionMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
mutationKey: ["model-connections", "update"],
|
||||
mutationFn: ({ id, data }: { id: number; data: ConnectionUpdateRequest }) =>
|
||||
|
|
@ -74,7 +74,7 @@ export const updateModelConnectionMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const deleteModelConnectionMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
mutationKey: ["model-connections", "delete"],
|
||||
mutationFn: (id: number) => modelConnectionsApiService.deleteConnection(id),
|
||||
|
|
@ -87,7 +87,7 @@ export const deleteModelConnectionMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const verifyModelConnectionMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
mutationKey: ["model-connections", "verify"],
|
||||
mutationFn: (id: number) => modelConnectionsApiService.verifyConnection(id),
|
||||
|
|
@ -110,7 +110,7 @@ export const verifyModelConnectionMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const discoverConnectionModelsMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
mutationKey: ["model-connections", "discover"],
|
||||
mutationFn: (id: number) => modelConnectionsApiService.discoverModels(id),
|
||||
|
|
@ -149,7 +149,7 @@ export const testPreviewModelMutationAtom = atomWithMutation(() => {
|
|||
});
|
||||
|
||||
export const addManualModelMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
mutationKey: ["models", "add-manual"],
|
||||
mutationFn: ({ connectionId, data }: { connectionId: number; data: ModelCreateRequest }) =>
|
||||
|
|
@ -163,7 +163,7 @@ export const addManualModelMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const updateModelMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
mutationKey: ["models", "update"],
|
||||
mutationFn: ({ id, data }: { id: number; data: ModelUpdateRequest }) =>
|
||||
|
|
@ -174,7 +174,7 @@ export const updateModelMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const bulkUpdateModelsMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
mutationKey: ["models", "bulk-update"],
|
||||
mutationFn: ({ connectionId, data }: { connectionId: number; data: ModelsBulkUpdateRequest }) =>
|
||||
|
|
@ -185,7 +185,7 @@ export const bulkUpdateModelsMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const testModelMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
mutationKey: ["models", "test"],
|
||||
mutationFn: (id: number) => modelConnectionsApiService.testModel(id),
|
||||
|
|
@ -199,7 +199,7 @@ export const testModelMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const updateModelRolesMutationAtom = atomWithMutation((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
mutationKey: ["model-roles", "update"],
|
||||
mutationFn: (roles: ModelRoles) =>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { atomWithQuery } from "jotai-tanstack-query";
|
|||
import { modelConnectionsApiService } from "@/lib/apis/model-connections-api.service";
|
||||
import { isAuthenticated } from "@/lib/auth-utils";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "../workspaces/workspace-query.atoms";
|
||||
|
||||
export const globalModelConnectionsAtom = atomWithQuery(() => ({
|
||||
queryKey: cacheKeys.modelConnections.global(),
|
||||
|
|
@ -26,7 +26,7 @@ export const modelProvidersAtom = atomWithQuery(() => ({
|
|||
}));
|
||||
|
||||
export const modelConnectionsAtom = atomWithQuery((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
queryKey: cacheKeys.modelConnections.all(searchSpaceId),
|
||||
enabled: !!searchSpaceId,
|
||||
|
|
@ -36,7 +36,7 @@ export const modelConnectionsAtom = atomWithQuery((get) => {
|
|||
});
|
||||
|
||||
export const modelRolesAtom = atomWithQuery((get) => {
|
||||
const searchSpaceId = Number(get(activeSearchSpaceIdAtom));
|
||||
const searchSpaceId = Number(get(activeWorkspaceIdAtom));
|
||||
return {
|
||||
queryKey: cacheKeys.modelConnections.roles(searchSpaceId),
|
||||
enabled: !!searchSpaceId,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { atomWithQuery } from "jotai-tanstack-query";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { chatThreadsApiService } from "@/lib/apis/chat-threads-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
|
||||
export const publicChatSnapshotsAtom = atomWithQuery((get) => {
|
||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
queryKey: cacheKeys.publicChatSnapshots.bySearchSpace(Number(searchSpaceId) || 0),
|
||||
|
|
@ -15,7 +15,7 @@ export const publicChatSnapshotsAtom = atomWithQuery((get) => {
|
|||
return { snapshots: [] };
|
||||
}
|
||||
return chatThreadsApiService.listPublicChatSnapshotsForSearchSpace({
|
||||
search_space_id: Number(searchSpaceId),
|
||||
workspace_id: Number(searchSpaceId),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export const createRoleMutationAtom = atomWithMutation(() => {
|
|||
onSuccess: (_: CreateRoleResponse, request: CreateRoleRequest) => {
|
||||
toast.success("Role created successfully");
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: cacheKeys.roles.all(request.search_space_id.toString()),
|
||||
queryKey: cacheKeys.roles.all(request.workspace_id.toString()),
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
|
|
@ -39,13 +39,10 @@ export const updateRoleMutationAtom = atomWithMutation(() => {
|
|||
onSuccess: (_: UpdateRoleResponse, request: UpdateRoleRequest) => {
|
||||
toast.success("Role updated successfully");
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: cacheKeys.roles.all(request.search_space_id.toString()),
|
||||
queryKey: cacheKeys.roles.all(request.workspace_id.toString()),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: cacheKeys.roles.byId(
|
||||
request.search_space_id.toString(),
|
||||
request.role_id.toString()
|
||||
),
|
||||
queryKey: cacheKeys.roles.byId(request.workspace_id.toString(), request.role_id.toString()),
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
|
|
@ -63,7 +60,7 @@ export const deleteRoleMutationAtom = atomWithMutation(() => {
|
|||
onSuccess: (_: DeleteRoleResponse, request: DeleteRoleRequest) => {
|
||||
toast.success("Role deleted successfully");
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: cacheKeys.roles.all(request.search_space_id.toString()),
|
||||
queryKey: cacheKeys.roles.all(request.workspace_id.toString()),
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import type {
|
|||
DeleteSearchSpaceRequest,
|
||||
UpdateSearchSpaceApiAccessRequest,
|
||||
UpdateSearchSpaceRequest,
|
||||
} from "@/contracts/types/search-space.types";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
} from "@/contracts/types/workspace.types";
|
||||
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { queryClient } from "@/lib/query-client/client";
|
||||
import { activeSearchSpaceIdAtom } from "./search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "./workspace-query.atoms";
|
||||
|
||||
export const createSearchSpaceMutationAtom = atomWithMutation(() => {
|
||||
return {
|
||||
|
|
@ -28,7 +28,7 @@ export const createSearchSpaceMutationAtom = atomWithMutation(() => {
|
|||
});
|
||||
|
||||
export const updateSearchSpaceMutationAtom = atomWithMutation((get) => {
|
||||
const activeSearchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const activeSearchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
mutationKey: ["update-search-space", activeSearchSpaceId],
|
||||
|
|
@ -52,7 +52,7 @@ export const updateSearchSpaceMutationAtom = atomWithMutation((get) => {
|
|||
});
|
||||
|
||||
export const updateSearchSpaceApiAccessMutationAtom = atomWithMutation((get) => {
|
||||
const activeSearchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const activeSearchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
mutationKey: ["update-search-space-api-access", activeSearchSpaceId],
|
||||
|
|
@ -74,7 +74,7 @@ export const updateSearchSpaceApiAccessMutationAtom = atomWithMutation((get) =>
|
|||
});
|
||||
|
||||
export const deleteSearchSpaceMutationAtom = atomWithMutation((get) => {
|
||||
const activeSearchSpaceId = get(activeSearchSpaceIdAtom);
|
||||
const activeSearchSpaceId = get(activeWorkspaceIdAtom);
|
||||
|
||||
return {
|
||||
mutationKey: ["delete-search-space", activeSearchSpaceId],
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
import { atom } from "jotai";
|
||||
import { atomWithQuery } from "jotai-tanstack-query";
|
||||
import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.types";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import type { GetSearchSpacesRequest } from "@/contracts/types/workspace.types";
|
||||
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
|
||||
export const activeSearchSpaceIdAtom = atom<string | null>(null);
|
||||
export const activeWorkspaceIdAtom = activeSearchSpaceIdAtom;
|
||||
export const activeWorkspaceIdAtom = atom<string | null>(null);
|
||||
|
||||
export const searchSpacesQueryParamsAtom = atom<GetSearchSpacesRequest["queryParams"]>({
|
||||
skip: 0,
|
||||
33
surfsense_web/changelog/content/2026-07-05.mdx
Normal file
33
surfsense_web/changelog/content/2026-07-05.mdx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
date: "2026-07-05"
|
||||
---
|
||||
|
||||
## SurfSense's Next Chapter: Competitive Intelligence for AI Agents
|
||||
|
||||
Happy Independence Day to everyone celebrating in the United States! We hope you had a wonderful Fourth of July. It felt like the right weekend for a declaration of our own.
|
||||
|
||||
Today we are pointing SurfSense at a single mission: **give your AI agents competitive intelligence.**
|
||||
|
||||
### How we got here
|
||||
|
||||
For the past couple of months we set out to build the best general research agent for your own knowledge. That chapter treated us well. As the open-source alternative to NotebookLM, SurfSense earned a community we are genuinely proud of: more than 15,000 GitHub stars, self-hosted deployments everywhere from homelabs to enterprises, and users who pushed the product further than we imagined.
|
||||
|
||||
That same period settled a bigger question. Agentic tools like Claude, OpenCode, Hermes, and OpenClaw have proven themselves. Agents that plan, use tools, and finish multi-step work on their own are no longer a demo, they are how work gets done. General research assistance is becoming something every capable agent does out of the box.
|
||||
|
||||
So we asked what these agents still lack. Not reasoning. Not tools. **Live market data and the workflows around it.** An agent asked "what are competitors charging this week?" or "what is Reddit saying about us since the launch?" has nowhere trustworthy to look. We want to be one of the first, and hopefully the definitive, open-source competitive intelligence agent platforms. This focus also lets our team pour all of our energy into the high-value workflows that genuinely need AI automation.
|
||||
|
||||
### What SurfSense is now
|
||||
|
||||
- **Platform-native connectors for live market data.** [Reddit](/reddit), [YouTube](/youtube), [Google Maps](/google-maps), [Google Search](/google-search), and the [open web](/web-crawl). Each one is a typed REST endpoint you can call from any language with your SurfSense API key.
|
||||
- **An MCP server that makes every connector an agent tool.** Add SurfSense to Claude, Cursor, or your own agent framework. And with the [MCP connector](/mcp-connector), your SurfSense agents can use any MCP server too, with one-click OAuth for apps like Notion, Slack, and Jira.
|
||||
- **An agent harness, not just raw data.** Retries, structured output, and credit metering are built in, so agents go from a question to a brief without you building the plumbing.
|
||||
- **Automations for the workflows that matter.** Scheduled and event-triggered agents watch competitor pricing, track brand mentions, monitor rankings and reviews, then deliver briefs and alerts.
|
||||
- **Simpler pricing.** Self-hosting stays free with no metering. Cloud is pay as you go: start with $5 of free credit, connectors bill per item returned, crawls per page fetched, models at provider cost. Failed calls are never billed. Details on the [pricing page](/pricing).
|
||||
|
||||
### To our existing users: nothing you rely on is going away
|
||||
|
||||
This is an addition, not a replacement. Your knowledge base, chat with citations, reports, podcasts, presentations, automations, and collaborative chats all keep working exactly as they do today. You can still upload files and sync Google Drive, OneDrive, and Dropbox. Self-hosting stays free and open source, and your existing deployments keep running without any action from you. What changes is where we invest: expect deeper connectors, sharper agent workflows, and broader market coverage.
|
||||
|
||||
If you are building agents, [grab an API key](/login) or add the MCP server, and start with the [connector catalog](/connectors). Tell us what you are building on [Discord](https://discord.gg/ejRNvftDp9) or [GitHub](https://github.com/MODSetter/SurfSense).
|
||||
|
||||
We think the next great competitive intelligence tool will not be a dashboard. It will be an agent that already knows your market. We are building the platform it runs on.
|
||||
|
|
@ -29,7 +29,7 @@ import {
|
|||
globalModelConnectionsAtom,
|
||||
modelConnectionsAtom,
|
||||
} from "@/atoms/model-connections/model-connections-query.atoms";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import {
|
||||
CitationMetadataProvider,
|
||||
useAllCitationMetadata,
|
||||
|
|
@ -491,7 +491,7 @@ export const AssistantMessage: FC = () => {
|
|||
const commentPanelRef = useRef<HTMLDivElement>(null);
|
||||
const commentTriggerRef = useRef<HTMLButtonElement>(null);
|
||||
const messageId = useAuiState(({ message }) => message?.id);
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const dbMessageId = parseMessageId(messageId);
|
||||
const commentsEnabled = useAtomValue(commentsEnabledAtom);
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
|
|||
import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { statusInboxItemsAtom } from "@/atoms/inbox/status-inbox.atom";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Tabs, TabsContent } from "@/components/ui/tabs";
|
||||
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
||||
|
|
@ -36,7 +36,7 @@ interface ConnectorIndicatorProps {
|
|||
|
||||
export const ConnectorIndicator = forwardRef<ConnectorIndicatorHandle, ConnectorIndicatorProps>(
|
||||
(_props, ref) => {
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
|
||||
// Real-time document type counts via Zero (updates instantly as docs are indexed)
|
||||
const documentTypeCounts = useZeroDocumentTypeCounts(searchSpaceId);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export interface CirclebackConfigProps extends ConnectorConfigProps {
|
|||
// Type-safe schema for webhook info response
|
||||
const circlebackWebhookInfoSchema = z.object({
|
||||
webhook_url: z.string(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
method: z.string(),
|
||||
content_type: z.string(),
|
||||
description: z.string(),
|
||||
|
|
@ -40,12 +40,12 @@ export const CirclebackConfig: FC<CirclebackConfigProps> = ({ connector, onNameC
|
|||
const controller = new AbortController();
|
||||
|
||||
const doFetch = async () => {
|
||||
if (!connector.search_space_id) return;
|
||||
if (!connector.workspace_id) return;
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await authenticatedFetch(
|
||||
buildBackendUrl(`/api/v1/webhooks/circleback/${connector.search_space_id}/info`),
|
||||
buildBackendUrl(`/api/v1/webhooks/circleback/${connector.workspace_id}/info`),
|
||||
{ signal: controller.signal }
|
||||
);
|
||||
if (controller.signal.aborted) return;
|
||||
|
|
@ -70,7 +70,7 @@ export const CirclebackConfig: FC<CirclebackConfigProps> = ({ connector, onNameC
|
|||
|
||||
doFetch().catch(() => {});
|
||||
return () => controller.abort();
|
||||
}, [connector.search_space_id]);
|
||||
}, [connector.workspace_id]);
|
||||
|
||||
const handleNameChange = (value: string) => {
|
||||
setName(value);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
|
|||
import { ArrowLeft, Info, RefreshCw } from "lucide-react";
|
||||
import { type FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
|
|
@ -78,7 +78,7 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
|
|||
onConfigChange,
|
||||
onNameChange,
|
||||
}) => {
|
||||
const searchSpaceIdAtom = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceIdAtom = useAtomValue(activeWorkspaceIdAtom);
|
||||
const isAuthExpired = connector.config?.auth_expired === true;
|
||||
const reauthEndpoint = getReauthEndpoint(connector);
|
||||
const [reauthing, setReauthing] = useState(false);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
updateConnectorMutationAtom,
|
||||
} from "@/atoms/connectors/connector-mutation.atoms";
|
||||
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { EnumConnectorName } from "@/contracts/enums/connector";
|
||||
import type { SearchSourceConnector } from "@/contracts/types/connector.types";
|
||||
import { searchSourceConnector } from "@/contracts/types/connector.types";
|
||||
|
|
@ -58,7 +58,7 @@ function clearOAuthResultCookie(): void {
|
|||
}
|
||||
|
||||
export const useConnectorDialog = () => {
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const { data: allConnectors, refetch: refetchAllConnectors } = useAtomValue(connectorsAtom);
|
||||
const { mutateAsync: indexConnector } = useAtomValue(indexConnectorMutationAtom);
|
||||
const { mutateAsync: updateConnector } = useAtomValue(updateConnectorMutationAtom);
|
||||
|
|
@ -165,7 +165,7 @@ export const useConnectorDialog = () => {
|
|||
await indexConnector({
|
||||
connector_id: connector.id,
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
start_date: format(startDate, "yyyy-MM-dd"),
|
||||
end_date: format(endDate, "yyyy-MM-dd"),
|
||||
},
|
||||
|
|
@ -418,7 +418,7 @@ export const useConnectorDialog = () => {
|
|||
enable_vision_llm: false,
|
||||
},
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -519,7 +519,7 @@ export const useConnectorDialog = () => {
|
|||
enable_vision_llm: false,
|
||||
},
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
},
|
||||
});
|
||||
// Refetch connectors to get the new one
|
||||
|
|
@ -612,7 +612,7 @@ export const useConnectorDialog = () => {
|
|||
await indexConnector({
|
||||
connector_id: connector.id,
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
start_date: startDateStr,
|
||||
end_date: endDateStr,
|
||||
},
|
||||
|
|
@ -847,7 +847,7 @@ export const useConnectorDialog = () => {
|
|||
await indexConnector({
|
||||
connector_id: indexingConfig.connectorId,
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
},
|
||||
body: {
|
||||
folders: selectedFolders || [],
|
||||
|
|
@ -870,14 +870,14 @@ export const useConnectorDialog = () => {
|
|||
await indexConnector({
|
||||
connector_id: indexingConfig.connectorId,
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await indexConnector({
|
||||
connector_id: indexingConfig.connectorId,
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
start_date: startDateStr,
|
||||
end_date: endDateStr,
|
||||
},
|
||||
|
|
@ -1114,7 +1114,7 @@ export const useConnectorDialog = () => {
|
|||
await indexConnector({
|
||||
connector_id: editingConnector.id,
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
},
|
||||
body: {
|
||||
folders: selectedFolders || [],
|
||||
|
|
@ -1134,7 +1134,7 @@ export const useConnectorDialog = () => {
|
|||
await indexConnector({
|
||||
connector_id: editingConnector.id,
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
},
|
||||
});
|
||||
indexingDescription = "Re-indexing started with updated configuration.";
|
||||
|
|
@ -1143,7 +1143,7 @@ export const useConnectorDialog = () => {
|
|||
await indexConnector({
|
||||
connector_id: editingConnector.id,
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
start_date: startDateStr,
|
||||
end_date: endDateStr,
|
||||
},
|
||||
|
|
@ -1296,7 +1296,7 @@ export const useConnectorDialog = () => {
|
|||
await indexConnector({
|
||||
connector_id: connectorId,
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
start_date: startDateStr,
|
||||
end_date: endDateStr,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
|
|||
import { ArrowLeft, Plus, RefreshCw, Server } from "lucide-react";
|
||||
import { type FC, useCallback, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { EnumConnectorName } from "@/contracts/enums/connector";
|
||||
|
|
@ -44,7 +44,7 @@ export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
|
|||
isConnecting = false,
|
||||
addButtonText,
|
||||
}) => {
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const [reauthingId, setReauthingId] = useState<number | null>(null);
|
||||
const [confirmDisconnectId, setConfirmDisconnectId] = useState<number | null>(null);
|
||||
const [disconnectingId, setDisconnectingId] = useState<number | null>(null);
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ export const YouTubeCrawlerView: FC<YouTubeCrawlerViewProps> = ({ searchSpaceId,
|
|||
{
|
||||
document_type: "YOUTUBE_VIDEO",
|
||||
content: videoUrls,
|
||||
search_space_id: parseInt(searchSpaceId, 10),
|
||||
workspace_id: parseInt(searchSpaceId, 10),
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { DocumentUploadTab } from "@/components/sources/DocumentUploadTab";
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -90,7 +90,7 @@ const DocumentUploadPopupContent: FC<{
|
|||
isOpen: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}> = ({ isOpen, onOpenChange }) => {
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
|
||||
if (!searchSpaceId) return null;
|
||||
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ function FilePathLink({ path, className }: { path: string; className?: string })
|
|||
if (!resolvedSearchSpaceId || !path.startsWith("/documents/")) return;
|
||||
try {
|
||||
const doc = await documentsApiService.getDocumentByVirtualPath({
|
||||
search_space_id: resolvedSearchSpaceId,
|
||||
workspace_id: resolvedSearchSpaceId,
|
||||
virtual_path: path,
|
||||
});
|
||||
openEditorPanel({
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ export const CitationPanelContent: FC<CitationPanelContentProps> = ({
|
|||
if (!data) return;
|
||||
openEditorPanel({
|
||||
documentId: data.id,
|
||||
searchSpaceId: data.search_space_id,
|
||||
searchSpaceId: data.workspace_id,
|
||||
title: data.title,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { BreadcrumbNav } from "@/components/seo/breadcrumb-nav";
|
|||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import type { ConnectorPageContent } from "@/lib/connectors-marketing/types";
|
||||
import type { ConnectorPageContent, SchemaField } from "@/lib/connectors-marketing/types";
|
||||
import { AgentTranscript } from "./agent-transcript";
|
||||
import { ApiMcpTabs } from "./api-mcp-tabs";
|
||||
import { ConnectorFaq } from "./connector-faq";
|
||||
|
|
@ -14,6 +14,46 @@ import { Reveal } from "./reveal";
|
|||
|
||||
const GITHUB_URL = "https://github.com/MODSetter/SurfSense";
|
||||
|
||||
function SchemaTable({ caption, fields }: { caption: string; fields: SchemaField[] }) {
|
||||
return (
|
||||
<div className="overflow-x-auto rounded-xl border bg-card">
|
||||
<table className="w-full min-w-xl text-sm">
|
||||
<caption className="sr-only">{caption}</caption>
|
||||
<thead>
|
||||
<tr className="border-b bg-muted/40 text-left">
|
||||
<th className="p-4 font-medium">Field</th>
|
||||
<th className="p-4 font-medium">Type</th>
|
||||
<th className="p-4 font-medium">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fields.map((field) => (
|
||||
<tr key={field.name} className="border-b align-top last:border-b-0">
|
||||
<th scope="row" className="p-4 text-left">
|
||||
<code className="font-mono text-[13px] font-semibold">{field.name}</code>
|
||||
</th>
|
||||
<td className="whitespace-nowrap p-4">
|
||||
<code className="font-mono text-[13px] text-muted-foreground">{field.type}</code>
|
||||
{field.required ? (
|
||||
<span className="ml-2 rounded-full bg-brand/10 px-2 py-0.5 text-xs font-medium text-brand">
|
||||
required
|
||||
</span>
|
||||
) : null}
|
||||
{field.defaultValue !== undefined ? (
|
||||
<div className="mt-1 text-xs text-muted-foreground">
|
||||
default <code className="font-mono">{field.defaultValue}</code>
|
||||
</div>
|
||||
) : null}
|
||||
</td>
|
||||
<td className="p-4 text-muted-foreground leading-relaxed">{field.description}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ConnectorPage({ content }: { content: ConnectorPageContent }) {
|
||||
const Icon = content.icon;
|
||||
const label = content.cardTitle ?? `${content.name} API`;
|
||||
|
|
@ -136,6 +176,44 @@ export function ConnectorPage({ content }: { content: ConnectorPageContent }) {
|
|||
</Reveal>
|
||||
</MarketingSection>
|
||||
|
||||
{/* Request / response schema */}
|
||||
<MarketingSection>
|
||||
<Reveal>
|
||||
<h2 className="text-2xl font-bold tracking-tight sm:text-3xl">
|
||||
{content.name} API request and response schema
|
||||
</h2>
|
||||
<p className="mt-3 max-w-2xl text-muted-foreground leading-relaxed">
|
||||
The exact contract behind{" "}
|
||||
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-sm">
|
||||
POST /workspaces/{"{workspace_id}"}/scrapers/{content.api.platform}/{content.api.verb}
|
||||
</code>
|
||||
. The same fields power the{" "}
|
||||
<code className="rounded bg-muted px-1.5 py-0.5 font-mono text-sm">
|
||||
{content.api.mcpTool}
|
||||
</code>{" "}
|
||||
MCP tool.
|
||||
</p>
|
||||
</Reveal>
|
||||
<Reveal>
|
||||
<h3 className="mt-8 text-lg font-semibold">Request parameters</h3>
|
||||
<p className="mt-2 max-w-2xl text-sm text-muted-foreground leading-relaxed">
|
||||
{content.schema.requestNote}
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<SchemaTable caption="Request parameters" fields={content.schema.request} />
|
||||
</div>
|
||||
</Reveal>
|
||||
<Reveal>
|
||||
<h3 className="mt-10 text-lg font-semibold">Response fields</h3>
|
||||
<p className="mt-2 max-w-2xl text-sm text-muted-foreground leading-relaxed">
|
||||
{content.schema.responseNote}
|
||||
</p>
|
||||
<div className="mt-4">
|
||||
<SchemaTable caption="Response fields" fields={content.schema.response} />
|
||||
</div>
|
||||
</Reveal>
|
||||
</MarketingSection>
|
||||
|
||||
{/* Comparison */}
|
||||
<MarketingSection>
|
||||
<Reveal>
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ export function EditorPanelContent({
|
|||
}
|
||||
const response = await authenticatedFetch(
|
||||
buildBackendUrl(
|
||||
`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/editor-content`
|
||||
`/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/editor-content`
|
||||
),
|
||||
{ method: "GET" }
|
||||
);
|
||||
|
|
@ -412,7 +412,7 @@ export function EditorPanelContent({
|
|||
throw new Error("Missing document context");
|
||||
}
|
||||
const response = await authenticatedFetch(
|
||||
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/save`),
|
||||
buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/save`),
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
|
@ -520,7 +520,7 @@ export function EditorPanelContent({
|
|||
try {
|
||||
const response = await authenticatedFetch(
|
||||
buildBackendUrl(
|
||||
`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/download-markdown`
|
||||
`/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/download-markdown`
|
||||
),
|
||||
{ method: "GET" }
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { IconBrandDiscord, IconBrandGithub } from "@tabler/icons-react";
|
||||
import { IconBrandDiscord, IconBrandGithub, IconBrandReddit } from "@tabler/icons-react";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Reveal } from "@/components/connectors-marketing/reveal";
|
||||
|
|
@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button";
|
|||
|
||||
const GITHUB_URL = "https://github.com/MODSetter/SurfSense";
|
||||
const DISCORD_URL = "https://discord.gg/ejRNvftDp9";
|
||||
const REDDIT_URL = "https://www.reddit.com/r/SurfSense/";
|
||||
|
||||
/** Closing CTA doubling as the GitHub/community strip (brief section 7). */
|
||||
export function CommunityStrip() {
|
||||
|
|
@ -40,6 +41,12 @@ export function CommunityStrip() {
|
|||
Join Discord
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="ghost" size="lg">
|
||||
<Link href={REDDIT_URL} target="_blank" rel="noopener noreferrer">
|
||||
<IconBrandReddit className="size-4" />
|
||||
r/SurfSense
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
IconBrandDiscord,
|
||||
IconBrandGithub,
|
||||
IconBrandLinkedin,
|
||||
IconBrandReddit,
|
||||
IconBrandTwitter,
|
||||
} from "@tabler/icons-react";
|
||||
import Link from "next/link";
|
||||
|
|
@ -68,6 +69,11 @@ export function FooterNew() {
|
|||
href: "https://discord.gg/ejRNvftDp9",
|
||||
icon: IconBrandDiscord,
|
||||
},
|
||||
{
|
||||
title: "Reddit",
|
||||
href: "https://www.reddit.com/r/SurfSense/",
|
||||
icon: IconBrandReddit,
|
||||
},
|
||||
];
|
||||
const legals = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ import { currentThreadAtom, resetCurrentThreadAtom } from "@/atoms/chat/current-
|
|||
import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
|
||||
import { statusInboxItemsAtom } from "@/atoms/inbox/status-inbox.atom";
|
||||
import { announcementsDialogAtom } from "@/atoms/layout/dialogs.atom";
|
||||
import { deleteSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms";
|
||||
import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { rightPanelCollapsedAtom } from "@/atoms/layout/right-panel.atom";
|
||||
import { removeChatTabAtom, syncChatTabAtom, type Tab } from "@/atoms/tabs/tabs.atom";
|
||||
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
|
||||
import { deleteSearchSpaceMutationAtom } from "@/atoms/workspaces/workspace-mutation.atoms";
|
||||
import { searchSpacesAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { ActionLogDialog } from "@/components/agent-action-log/action-log-dialog";
|
||||
import { AnnouncementSpotlight } from "@/components/announcements/AnnouncementSpotlight";
|
||||
import { AnnouncementsDialog } from "@/components/announcements/AnnouncementsDialog";
|
||||
|
|
@ -46,7 +47,7 @@ import { useInbox } from "@/hooks/use-inbox";
|
|||
import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import { useArchiveThread, useDeleteThread, useRenameThread } from "@/hooks/use-thread-mutations";
|
||||
import { notificationsApiService } from "@/lib/apis/notifications-api.service";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
|
||||
import { getLoginPath, logout } from "@/lib/auth-utils";
|
||||
import { fetchThreads } from "@/lib/chat/thread-persistence";
|
||||
import { resetUser, trackLogout } from "@/lib/posthog/events";
|
||||
|
|
@ -406,7 +407,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
|
|||
|
||||
const handleSearchSpaceSettings = useCallback(
|
||||
(space: SearchSpace) => {
|
||||
router.push(`/dashboard/${space.id}/search-space-settings/general`);
|
||||
router.push(`/dashboard/${space.id}/workspace-settings`);
|
||||
},
|
||||
[router]
|
||||
);
|
||||
|
|
@ -594,7 +595,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
|
|||
);
|
||||
|
||||
const handleSettings = useCallback(() => {
|
||||
router.push(`/dashboard/${searchSpaceId}/search-space-settings/general`);
|
||||
router.push(`/dashboard/${searchSpaceId}/workspace-settings`);
|
||||
}, [router, searchSpaceId]);
|
||||
|
||||
const handleManageMembers = useCallback(() => {
|
||||
|
|
@ -694,7 +695,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
|
|||
// Detect if we're on the chat page (needs overflow-hidden for chat's own scroll)
|
||||
const isChatPage = pathname?.includes("/new-chat") ?? false;
|
||||
const isUserSettingsPage = pathname?.includes("/user-settings") === true;
|
||||
const isSearchSpaceSettingsPage = pathname?.includes("/search-space-settings") === true;
|
||||
const isSearchSpaceSettingsPage = pathname?.includes("/workspace-settings") === true;
|
||||
const isTeamPage = pathname?.endsWith("/team") === true;
|
||||
const isAutomationsPage = pathname?.includes("/automations") === true;
|
||||
const isAllChatsPage = pathname?.endsWith("/chats") === true;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ 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 { createSearchSpaceMutationAtom } from "@/atoms/workspaces/workspace-mutation.atoms";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -1 +1 @@
|
|||
export { CreateSearchSpaceDialog } from "./CreateSearchSpaceDialog";
|
||||
export { CreateSearchSpaceDialog } from "./CreateWorkspaceDialog";
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
import { useAtomValue } from "jotai";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { currentThreadAtom } from "@/atoms/chat/current-thread.atom";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeTabAtom } from "@/atoms/tabs/tabs.atom";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { ActionLogButton } from "@/components/agent-action-log/action-log-button";
|
||||
import { ChatShareButton } from "@/components/new-chat/chat-share-button";
|
||||
import { ArtifactsToggleButton } from "@/features/chat-artifacts";
|
||||
|
|
@ -16,7 +16,7 @@ interface HeaderProps {
|
|||
|
||||
export function Header({ mobileMenuTrigger }: HeaderProps) {
|
||||
const pathname = usePathname();
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const activeTab = useAtomValue(activeTabAtom);
|
||||
|
||||
const isFreePage = pathname?.startsWith("/free") ?? false;
|
||||
|
|
@ -56,7 +56,7 @@ export function Header({ mobileMenuTrigger }: HeaderProps) {
|
|||
id: currentThreadState.id,
|
||||
visibility: currentThreadState.visibility,
|
||||
created_by_id: null,
|
||||
search_space_id: currentThreadState.searchSpaceId,
|
||||
workspace_id: currentThreadState.searchSpaceId,
|
||||
title: "",
|
||||
archived: false,
|
||||
created_at: "",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
|
|||
import { cn } from "@/lib/utils";
|
||||
import type { NavItem, SearchSpace, User } from "../../types/layout.types";
|
||||
import { SidebarUserProfile } from "../sidebar/SidebarUserProfile";
|
||||
import { SearchSpaceAvatar } from "./SearchSpaceAvatar";
|
||||
import { SearchSpaceAvatar } from "./WorkspaceAvatar";
|
||||
|
||||
interface IconRailProps {
|
||||
searchSpaces: SearchSpace[];
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
export { IconRail } from "./IconRail";
|
||||
export { NavIcon } from "./NavIcon";
|
||||
export { SearchSpaceAvatar } from "./SearchSpaceAvatar";
|
||||
export { SearchSpaceAvatar } from "./WorkspaceAvatar";
|
||||
|
|
|
|||
|
|
@ -8,10 +8,16 @@ import { useTranslations } from "next-intl";
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { makeFolderMention, mentionedDocumentsAtom } from "@/atoms/chat/mentioned-documents.atom";
|
||||
import { connectorDialogOpenAtom } from "@/atoms/connector-dialog/connector-dialog.atoms";
|
||||
import { deleteDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms";
|
||||
import { expandedFolderIdsAtom } from "@/atoms/documents/folder.atoms";
|
||||
import { agentCreatedDocumentsAtom } from "@/atoms/documents/ui.atoms";
|
||||
import { openEditorPanelAtom } from "@/atoms/editor/editor-panel.atom";
|
||||
import {
|
||||
folderWatchDialogOpenAtom,
|
||||
folderWatchInitialFolderAtom,
|
||||
} from "@/atoms/folder-sync/folder-sync.atoms";
|
||||
import { searchSpacesAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { CreateFolderDialog } from "@/components/documents/CreateFolderDialog";
|
||||
import type { DocumentNodeDoc } from "@/components/documents/DocumentNode";
|
||||
import { getDocumentTypeIcon } from "@/components/documents/DocumentTypeIcon";
|
||||
|
|
@ -51,6 +57,7 @@ import type { DocumentTypeEnum } from "@/contracts/types/document.types";
|
|||
import { useElectronAPI, usePlatform } from "@/hooks/use-platform";
|
||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||
import { foldersApiService } from "@/lib/apis/folders-api.service";
|
||||
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-fetch";
|
||||
import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
|
||||
import { getDocumentTypeLabel } from "@/lib/documents/document-type-labels";
|
||||
|
|
@ -234,7 +241,8 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
const platformElectronAPI = useElectronAPI();
|
||||
const electronAPI = desktopFeaturesEnabled ? platformElectronAPI : null;
|
||||
const { etlService } = useRuntimeConfig();
|
||||
const searchSpaceId = getWorkspaceIdNumber(params) ?? 0;
|
||||
const workspaceId = getWorkspaceIdNumber(params) ?? 0;
|
||||
const setConnectorDialogOpen = useSetAtom(connectorDialogOpenAtom);
|
||||
const openEditorPanel = useSetAtom(openEditorPanelAtom);
|
||||
|
||||
const [activeTypes, setActiveTypes] = useState<DocumentTypeEnum[]>([]);
|
||||
|
|
@ -248,7 +256,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
|
||||
if (folders.length === 0) {
|
||||
try {
|
||||
const backendFolders = await documentsApiService.getWatchedFolders(searchSpaceId);
|
||||
const backendFolders = await documentsApiService.getWatchedFolders(workspaceId);
|
||||
for (const bf of backendFolders) {
|
||||
const meta = bf.metadata as Record<string, unknown> | null;
|
||||
if (!meta?.watched || !meta.folder_path) continue;
|
||||
|
|
@ -256,7 +264,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
path: meta.folder_path as string,
|
||||
name: bf.name,
|
||||
rootFolderId: bf.id,
|
||||
searchSpaceId: bf.search_space_id,
|
||||
searchSpaceId: bf.workspace_id,
|
||||
excludePatterns: (meta.exclude_patterns as string[]) ?? [],
|
||||
fileExtensions: (meta.file_extensions as string[] | null) ?? null,
|
||||
active: true,
|
||||
|
|
@ -281,7 +289,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
.map((f: WatchedFolderEntry) => f.rootFolderId as number)
|
||||
);
|
||||
setWatchedFolderIds(ids);
|
||||
}, [searchSpaceId, electronAPI]);
|
||||
}, [workspaceId, electronAPI]);
|
||||
|
||||
useEffect(() => {
|
||||
refreshWatchedIds();
|
||||
|
|
@ -297,24 +305,28 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
// Folder state
|
||||
const [expandedFolderMap, setExpandedFolderMap] = useAtom(expandedFolderIdsAtom);
|
||||
const expandedIds = useMemo(
|
||||
() => new Set(expandedFolderMap[searchSpaceId] ?? []),
|
||||
[expandedFolderMap, searchSpaceId]
|
||||
() => new Set(expandedFolderMap[workspaceId] ?? []),
|
||||
[expandedFolderMap, workspaceId]
|
||||
);
|
||||
const toggleFolderExpand = useCallback(
|
||||
(folderId: number) => {
|
||||
setExpandedFolderMap((prev) => {
|
||||
const current = new Set(prev[searchSpaceId] ?? []);
|
||||
const current = new Set(prev[workspaceId] ?? []);
|
||||
if (current.has(folderId)) current.delete(folderId);
|
||||
else current.add(folderId);
|
||||
return { ...prev, [searchSpaceId]: [...current] };
|
||||
return { ...prev, [workspaceId]: [...current] };
|
||||
});
|
||||
},
|
||||
[searchSpaceId, setExpandedFolderMap]
|
||||
[workspaceId, setExpandedFolderMap]
|
||||
);
|
||||
|
||||
// Zero queries for tree data
|
||||
const [zeroFolders, zeroFoldersResult] = useQuery(queries.folders.bySpace({ searchSpaceId }));
|
||||
const [zeroAllDocs, zeroAllDocsResult] = useQuery(queries.documents.bySpace({ searchSpaceId }));
|
||||
const [zeroFolders, zeroFoldersResult] = useQuery(
|
||||
queries.folders.bySpace({ searchSpaceId: workspaceId })
|
||||
);
|
||||
const [zeroAllDocs, zeroAllDocsResult] = useQuery(
|
||||
queries.documents.bySpace({ searchSpaceId: workspaceId })
|
||||
);
|
||||
const [agentCreatedDocs, setAgentCreatedDocs] = useAtom(agentCreatedDocumentsAtom);
|
||||
|
||||
const treeFolders: FolderDisplay[] = useMemo(
|
||||
|
|
@ -349,7 +361,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
const zeroIds = new Set(zeroDocs.map((d) => d.id));
|
||||
|
||||
const pendingAgentDocs = agentCreatedDocs
|
||||
.filter((d) => d.searchSpaceId === searchSpaceId && !zeroIds.has(d.id))
|
||||
.filter((d) => d.searchSpaceId === workspaceId && !zeroIds.has(d.id))
|
||||
.map((d) => ({
|
||||
id: d.id,
|
||||
title: d.title,
|
||||
|
|
@ -359,7 +371,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
}));
|
||||
|
||||
return [...pendingAgentDocs, ...zeroDocs];
|
||||
}, [zeroAllDocs, agentCreatedDocs, searchSpaceId]);
|
||||
}, [zeroAllDocs, agentCreatedDocs, workspaceId]);
|
||||
|
||||
// Prune agent-created docs once Zero has caught up
|
||||
useEffect(() => {
|
||||
|
|
@ -409,21 +421,21 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
await foldersApiService.createFolder({
|
||||
name,
|
||||
parent_id: createFolderParentId,
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: workspaceId,
|
||||
});
|
||||
toast.success("Folder created");
|
||||
if (createFolderParentId !== null) {
|
||||
setExpandedFolderMap((prev) => {
|
||||
const current = new Set(prev[searchSpaceId] ?? []);
|
||||
const current = new Set(prev[workspaceId] ?? []);
|
||||
current.add(createFolderParentId);
|
||||
return { ...prev, [searchSpaceId]: [...current] };
|
||||
return { ...prev, [workspaceId]: [...current] };
|
||||
});
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
toast.error((e as Error)?.message || "Failed to create folder");
|
||||
}
|
||||
},
|
||||
[createFolderParentId, searchSpaceId, setExpandedFolderMap]
|
||||
[createFolderParentId, workspaceId, setExpandedFolderMap]
|
||||
);
|
||||
|
||||
const handleRescanFolder = useCallback(
|
||||
|
|
@ -444,7 +456,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
await uploadFolderScan({
|
||||
folderPath: matched.path,
|
||||
folderName: matched.name,
|
||||
searchSpaceId,
|
||||
searchSpaceId: workspaceId,
|
||||
excludePatterns: matched.excludePatterns ?? DEFAULT_EXCLUDE_PATTERNS,
|
||||
fileExtensions:
|
||||
matched.fileExtensions ?? Array.from(getSupportedExtensionsSet(undefined, etlService)),
|
||||
|
|
@ -455,7 +467,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
toast.error((err as Error)?.message || "Failed to re-scan folder");
|
||||
}
|
||||
},
|
||||
[searchSpaceId, electronAPI, etlService]
|
||||
[workspaceId, electronAPI, etlService]
|
||||
);
|
||||
|
||||
const handleStopWatching = useCallback(
|
||||
|
|
@ -577,7 +589,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
.trim()
|
||||
.slice(0, 80) || "folder";
|
||||
await doExport(
|
||||
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/export`, {
|
||||
buildBackendUrl(`/api/v1/workspaces/${workspaceId}/export`, {
|
||||
folder_id: ctx.folder.id,
|
||||
}),
|
||||
`${safeName}.zip`
|
||||
|
|
@ -590,7 +602,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
isExportingKBRef.current = false;
|
||||
}
|
||||
setExportWarningContext(null);
|
||||
}, [exportWarningContext, searchSpaceId, doExport]);
|
||||
}, [exportWarningContext, workspaceId, doExport]);
|
||||
|
||||
const getPendingCountInSubtree = useCallback(
|
||||
(folderId: number): number => {
|
||||
|
|
@ -631,7 +643,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
.trim()
|
||||
.slice(0, 80) || "folder";
|
||||
await doExport(
|
||||
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/export`, {
|
||||
buildBackendUrl(`/api/v1/workspaces/${workspaceId}/export`, {
|
||||
folder_id: folder.id,
|
||||
}),
|
||||
`${safeName}.zip`
|
||||
|
|
@ -644,7 +656,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
isExportingKBRef.current = false;
|
||||
}
|
||||
},
|
||||
[searchSpaceId, getPendingCountInSubtree, doExport]
|
||||
[workspaceId, getPendingCountInSubtree, doExport]
|
||||
);
|
||||
|
||||
const handleExportDocument = useCallback(
|
||||
|
|
@ -654,7 +666,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
const endpoint =
|
||||
doc.document_type === "USER_MEMORY"
|
||||
? buildBackendUrl("/api/v1/users/me/memory")
|
||||
: buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/memory`);
|
||||
: buildBackendUrl(`/api/v1/workspaces/${workspaceId}/memory`);
|
||||
const response = await authenticatedFetch(endpoint, { method: "GET" });
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({ detail: "Export failed" }));
|
||||
|
|
@ -682,7 +694,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
|
||||
try {
|
||||
const response = await authenticatedFetch(
|
||||
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/documents/${doc.id}/export`, {
|
||||
buildBackendUrl(`/api/v1/workspaces/${workspaceId}/documents/${doc.id}/export`, {
|
||||
format,
|
||||
}),
|
||||
{ method: "GET" }
|
||||
|
|
@ -707,7 +719,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
toast.error(err instanceof Error ? err.message : `Export failed`);
|
||||
}
|
||||
},
|
||||
[searchSpaceId]
|
||||
[workspaceId]
|
||||
);
|
||||
|
||||
const handleFolderPickerSelect = useCallback(
|
||||
|
|
@ -830,7 +842,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
openEditorPanel({
|
||||
kind: "memory",
|
||||
memoryScope: "user",
|
||||
searchSpaceId,
|
||||
searchSpaceId: workspaceId,
|
||||
title: doc.title,
|
||||
});
|
||||
return true;
|
||||
|
|
@ -839,14 +851,14 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
openEditorPanel({
|
||||
kind: "memory",
|
||||
memoryScope: "team",
|
||||
searchSpaceId,
|
||||
searchSpaceId: workspaceId,
|
||||
title: doc.title,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[openEditorPanel, searchSpaceId]
|
||||
[openEditorPanel, workspaceId]
|
||||
);
|
||||
|
||||
const handleResetMemoryDocument = useCallback(
|
||||
|
|
@ -858,7 +870,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
const endpoint =
|
||||
doc.document_type === "USER_MEMORY"
|
||||
? buildBackendUrl("/api/v1/users/me/memory/reset")
|
||||
: buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/memory/reset`);
|
||||
: buildBackendUrl(`/api/v1/workspaces/${workspaceId}/memory/reset`);
|
||||
try {
|
||||
const response = await authenticatedFetch(endpoint, { method: "POST" });
|
||||
if (!response.ok) {
|
||||
|
|
@ -871,7 +883,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
toast.error((error as Error)?.message || `Failed to reset ${doc.title.toLowerCase()}`);
|
||||
}
|
||||
},
|
||||
[openMemoryDocument, searchSpaceId]
|
||||
[openMemoryDocument, workspaceId]
|
||||
);
|
||||
|
||||
const typeCounts = useMemo(() => {
|
||||
|
|
@ -1008,7 +1020,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
if (openMemoryDocument(doc)) return;
|
||||
openEditorPanel({
|
||||
documentId: doc.id,
|
||||
searchSpaceId,
|
||||
searchSpaceId: workspaceId,
|
||||
title: doc.title,
|
||||
});
|
||||
}}
|
||||
|
|
@ -1016,7 +1028,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
if (openMemoryDocument(doc)) return;
|
||||
openEditorPanel({
|
||||
documentId: doc.id,
|
||||
searchSpaceId,
|
||||
searchSpaceId: workspaceId,
|
||||
title: doc.title,
|
||||
});
|
||||
}}
|
||||
|
|
@ -1098,7 +1110,7 @@ function AuthenticatedDocumentsSidebarBase({
|
|||
{deletableSelectedIds.length === 1
|
||||
? "This document"
|
||||
: `These ${deletableSelectedIds.length} documents`}{" "}
|
||||
will be permanently deleted from your search space.
|
||||
will be permanently deleted from your workspace.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ export function InboxSidebarContent({
|
|||
queryFn: () =>
|
||||
notificationsApiService.getNotifications({
|
||||
queryParams: {
|
||||
search_space_id: searchSpaceId ?? undefined,
|
||||
workspace_id: searchSpaceId ?? undefined,
|
||||
type: searchTypeFilter,
|
||||
search: debouncedSearch.trim(),
|
||||
limit: 50,
|
||||
|
|
@ -364,7 +364,7 @@ export function InboxSidebarContent({
|
|||
|
||||
if (item.type === "new_mention") {
|
||||
if (isNewMentionMetadata(item.metadata)) {
|
||||
const searchSpaceId = item.search_space_id;
|
||||
const searchSpaceId = item.workspace_id;
|
||||
const threadId = item.metadata.thread_id;
|
||||
const commentId = item.metadata.comment_id;
|
||||
|
||||
|
|
@ -382,7 +382,7 @@ export function InboxSidebarContent({
|
|||
}
|
||||
} else if (item.type === "comment_reply") {
|
||||
if (isCommentReplyMetadata(item.metadata)) {
|
||||
const searchSpaceId = item.search_space_id;
|
||||
const searchSpaceId = item.workspace_id;
|
||||
const threadId = item.metadata.thread_id;
|
||||
const replyId = item.metadata.reply_id;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
|
||||
import type { ChatItem, NavItem, PageUsage, SearchSpace, User } from "../../types/layout.types";
|
||||
import { SearchSpaceAvatar } from "../icon-rail/SearchSpaceAvatar";
|
||||
import { SearchSpaceAvatar } from "../icon-rail/WorkspaceAvatar";
|
||||
import { Sidebar } from "./Sidebar";
|
||||
|
||||
interface MobileSidebarProps {
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ export function DocumentTabContent({ documentId, searchSpaceId, title }: Documen
|
|||
try {
|
||||
const response = await authenticatedFetch(
|
||||
buildBackendUrl(
|
||||
`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/editor-content`
|
||||
`/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/editor-content`
|
||||
),
|
||||
{ method: "GET" }
|
||||
);
|
||||
|
|
@ -154,7 +154,7 @@ export function DocumentTabContent({ documentId, searchSpaceId, title }: Documen
|
|||
setSaving(true);
|
||||
try {
|
||||
const response = await authenticatedFetch(
|
||||
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/save`),
|
||||
buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/save`),
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
|
@ -313,7 +313,7 @@ export function DocumentTabContent({ documentId, searchSpaceId, title }: Documen
|
|||
try {
|
||||
const response = await authenticatedFetch(
|
||||
buildBackendUrl(
|
||||
`/api/v1/search-spaces/${searchSpaceId}/documents/${documentId}/download-markdown`
|
||||
`/api/v1/workspaces/${searchSpaceId}/documents/${documentId}/download-markdown`
|
||||
),
|
||||
{ method: "GET" }
|
||||
);
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
|
|||
|
||||
// Use Jotai atom for visibility (single source of truth)
|
||||
const currentThreadState = useAtomValue(currentThreadAtom);
|
||||
const { mutateAsync: updateVisibility } = useUpdateThreadVisibility(thread?.search_space_id ?? 0);
|
||||
const { mutateAsync: updateVisibility } = useUpdateThreadVisibility(thread?.workspace_id ?? 0);
|
||||
|
||||
// Snapshot creation mutation
|
||||
const { mutateAsync: createSnapshot, isPending: isCreatingSnapshot } = useAtomValue(
|
||||
|
|
@ -139,9 +139,7 @@ export function ChatShareButton({ thread, onVisibilityChange, className }: ChatS
|
|||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/dashboard/${thread.search_space_id}/search-space-settings/public-links`
|
||||
)
|
||||
router.push(`/dashboard/${thread.workspace_id}/workspace-settings/public-links`)
|
||||
}
|
||||
className="size-8 bg-muted/50 hover:bg-accent hover:text-accent-foreground"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ export const DocumentMentionPicker = forwardRef<
|
|||
|
||||
const titleSearchParams = useMemo(
|
||||
() => ({
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
page: 0,
|
||||
page_size: PAGE_SIZE,
|
||||
...(isSearchValid ? { title: debouncedSearch.trim() } : {}),
|
||||
|
|
@ -362,7 +362,7 @@ export const DocumentMentionPicker = forwardRef<
|
|||
|
||||
try {
|
||||
const queryParams = {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
page: nextPage,
|
||||
page_size: PAGE_SIZE,
|
||||
...(isSearchValid ? { title: debouncedSearch.trim() } : {}),
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ export function ImageModelSelector({
|
|||
|
||||
function manageModelConnections() {
|
||||
setOpen(false);
|
||||
router.push(`/dashboard/${searchSpaceId}/search-space-settings/models`);
|
||||
router.push(`/dashboard/${searchSpaceId}/workspace-settings/models`);
|
||||
}
|
||||
|
||||
const handleScroll = useCallback((event: UIEvent<HTMLDivElement>) => {
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ export function ModelSelector({
|
|||
|
||||
function manageModelConnections() {
|
||||
setOpen(false);
|
||||
router.push(`/dashboard/${searchSpaceId}/search-space-settings/models`);
|
||||
router.push(`/dashboard/${searchSpaceId}/workspace-settings/models`);
|
||||
}
|
||||
|
||||
const handleScroll = useCallback((event: UIEvent<HTMLDivElement>) => {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import { useTheme } from "next-themes";
|
|||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useIsMobile } from "@/hooks/use-mobile";
|
||||
import { useZeroDocumentTypeCounts } from "@/hooks/use-zero-document-type-counts";
|
||||
|
|
@ -391,7 +391,7 @@ export function OnboardingTour() {
|
|||
|
||||
// Get user data
|
||||
const { data: user } = useAtomValue(currentUserAtom);
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
|
||||
// Fetch threads data
|
||||
const { data: threadsData } = useQuery({
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export function PublicChatFooter({ shareToken }: PublicChatFooterProps) {
|
|||
});
|
||||
|
||||
// Redirect to the new chat page with cloned content
|
||||
router.push(`/dashboard/${response.search_space_id}/new-chat/${response.thread_id}`);
|
||||
router.push(`/dashboard/${response.workspace_id}/new-chat/${response.thread_id}`);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "Failed to copy chat";
|
||||
toast.error(message);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,12 @@ export function OrganizationJsonLd() {
|
|||
logo: "https://www.surfsense.com/logo.png",
|
||||
description:
|
||||
"SurfSense is an open-source competitive intelligence platform. AI agents monitor competitors, track rankings, and listen to your market through one API or MCP server.",
|
||||
sameAs: ["https://github.com/MODSetter/SurfSense", "https://discord.gg/ejRNvftDp9"],
|
||||
sameAs: [
|
||||
"https://github.com/MODSetter/SurfSense",
|
||||
"https://discord.gg/ejRNvftDp9",
|
||||
"https://www.reddit.com/r/SurfSense/",
|
||||
"https://www.linkedin.com/company/surfsense/",
|
||||
],
|
||||
contactPoint: {
|
||||
"@type": "ContactPoint",
|
||||
email: "rohan@surfsense.com",
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export function AutoReloadSettings() {
|
|||
|
||||
const setupMutation = useMutation({
|
||||
mutationFn: () =>
|
||||
stripeApiService.createAutoReloadSetupSession({ search_space_id: searchSpaceId }),
|
||||
stripeApiService.createAutoReloadSetupSession({ workspace_id: searchSpaceId }),
|
||||
onSuccess: (response) => {
|
||||
window.location.assign(response.checkout_url);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ export function BuyCreditsContent() {
|
|||
<Button
|
||||
className="w-full"
|
||||
disabled={purchaseMutation.isPending}
|
||||
onClick={() => purchaseMutation.mutate({ quantity, search_space_id: searchSpaceId })}
|
||||
onClick={() => purchaseMutation.mutate({ quantity, workspace_id: searchSpaceId })}
|
||||
>
|
||||
{purchaseMutation.isPending ? (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ import { toast } from "sonner";
|
|||
import {
|
||||
updateSearchSpaceApiAccessMutationAtom,
|
||||
updateSearchSpaceMutationAtom,
|
||||
} from "@/atoms/search-spaces/search-space-mutation.atoms";
|
||||
} from "@/atoms/workspaces/workspace-mutation.atoms";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-fetch";
|
||||
import { buildBackendUrl } from "@/lib/env-config";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
|
|
@ -58,7 +58,7 @@ export function GeneralSettingsManager({ workspaceId }: GeneralSettingsManagerPr
|
|||
setIsExporting(true);
|
||||
try {
|
||||
const response = await authenticatedFetch(
|
||||
buildBackendUrl(`/api/v1/search-spaces/${searchSpaceId}/export`),
|
||||
buildBackendUrl(`/api/v1/workspaces/${searchSpaceId}/export`),
|
||||
{ method: "GET" }
|
||||
);
|
||||
if (!response.ok) {
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ export function ConnectionSettingsDialog({
|
|||
base_url: data.base_url,
|
||||
api_key: apiKeyForTest,
|
||||
scope: "SEARCH_SPACE",
|
||||
search_space_id: connection.search_space_id,
|
||||
workspace_id: connection.workspace_id,
|
||||
extra: connection.extra ?? {},
|
||||
enabled: connection.enabled,
|
||||
models: [],
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ export function ModelProviderConnectionsPanel({
|
|||
base_url: draft.base_url,
|
||||
api_key: draft.api_key,
|
||||
scope: "SEARCH_SPACE" as const,
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
extra: draft.extra,
|
||||
enabled: true,
|
||||
models,
|
||||
|
|
@ -181,7 +181,7 @@ export function ModelProviderConnectionsPanel({
|
|||
base_url: null,
|
||||
api_key: null,
|
||||
scope: "SEARCH_SPACE",
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
extra: {},
|
||||
enabled: true,
|
||||
models: [],
|
||||
|
|
@ -205,7 +205,7 @@ export function ModelProviderConnectionsPanel({
|
|||
base_url: draft.base_url,
|
||||
api_key: draft.api_key,
|
||||
scope: "SEARCH_SPACE",
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
extra: draft.extra,
|
||||
enabled: true,
|
||||
models: [],
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ import { useAtomValue } from "jotai";
|
|||
import { AlertTriangle, Info } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { updateSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms";
|
||||
import { updateSearchSpaceMutationAtom } from "@/atoms/workspaces/workspace-mutation.atoms";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { searchSpacesApiService } from "@/lib/apis/workspaces-api.service";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { Spinner } from "../ui/spinner";
|
||||
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ export function RolesManager({ workspaceId }: { workspaceId: number }) {
|
|||
|
||||
const { data: roles = [], isLoading: rolesLoading } = useQuery({
|
||||
queryKey: cacheKeys.roles.all(searchSpaceId.toString()),
|
||||
queryFn: () => rolesApiService.getRoles({ search_space_id: searchSpaceId }),
|
||||
queryFn: () => rolesApiService.getRoles({ workspace_id: searchSpaceId }),
|
||||
enabled: !!searchSpaceId,
|
||||
});
|
||||
|
||||
|
|
@ -312,7 +312,7 @@ export function RolesManager({ workspaceId }: { workspaceId: number }) {
|
|||
}
|
||||
): Promise<Role> => {
|
||||
const request: UpdateRoleRequest = {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
role_id: roleId,
|
||||
data: data,
|
||||
};
|
||||
|
|
@ -324,7 +324,7 @@ export function RolesManager({ workspaceId }: { workspaceId: number }) {
|
|||
const handleDeleteRole = useCallback(
|
||||
async (roleId: number): Promise<boolean> => {
|
||||
const request: DeleteRoleRequest = {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
role_id: roleId,
|
||||
};
|
||||
await deleteRole(request);
|
||||
|
|
@ -336,7 +336,7 @@ export function RolesManager({ workspaceId }: { workspaceId: number }) {
|
|||
const handleCreateRole = useCallback(
|
||||
async (roleData: CreateRoleRequest["data"]): Promise<Role> => {
|
||||
const request: CreateRoleRequest = {
|
||||
search_space_id: searchSpaceId,
|
||||
workspace_id: searchSpaceId,
|
||||
data: roleData,
|
||||
};
|
||||
return await createRole(request);
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ export function DocumentUploadTab({
|
|||
batch.map((e) => e.file),
|
||||
{
|
||||
folder_name: folderUpload.folderName,
|
||||
search_space_id: Number(searchSpaceId),
|
||||
workspace_id: Number(searchSpaceId),
|
||||
relative_paths: batch.map((e) => e.relativePath),
|
||||
root_folder_id: rootFolderId,
|
||||
use_vision_llm: useVisionLlm,
|
||||
|
|
@ -413,7 +413,7 @@ export function DocumentUploadTab({
|
|||
uploadDocuments(
|
||||
{
|
||||
files: rawFiles,
|
||||
search_space_id: Number(searchSpaceId),
|
||||
workspace_id: Number(searchSpaceId),
|
||||
use_vision_llm: useVisionLlm,
|
||||
processing_mode: processingMode,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
AutomationModelFields,
|
||||
type AutomationModelSelection,
|
||||
} from "@/app/dashboard/[workspace_id]/automations/components/builder/automation-model-fields";
|
||||
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
|
||||
import { JsonView } from "@/components/json-view";
|
||||
import { TextShimmerLoader } from "@/components/prompt-kit/loader";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
|
|
@ -27,7 +27,7 @@ import {
|
|||
} from "@/lib/posthog/events";
|
||||
import { AutomationDraftPreview } from "./automation-draft-preview";
|
||||
|
||||
const editArgsSchema = automationCreateRequest.omit({ search_space_id: true });
|
||||
const editArgsSchema = automationCreateRequest.omit({ workspace_id: true });
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Result discrimination — mirrors the backend return shapes in
|
||||
|
|
@ -35,7 +35,7 @@ const editArgsSchema = automationCreateRequest.omit({ search_space_id: true });
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
type AutomationCreateContext = {
|
||||
search_space_id?: number;
|
||||
workspace_id?: number;
|
||||
};
|
||||
|
||||
interface SavedResult {
|
||||
|
|
@ -81,7 +81,7 @@ function hasStatus(value: unknown, status: string): boolean {
|
|||
//
|
||||
// Edit toggle reuses the same primitives as the Create-via-JSON page: raw
|
||||
// textarea, Format, Zod validation against ``AutomationCreate`` (minus the
|
||||
// ``search_space_id`` field, which the backend injects). Approve dispatches
|
||||
// ``workspace_id`` field, which the backend injects). Approve dispatches
|
||||
// an ``edit`` decision with the parsed args when edits are pending, otherwise
|
||||
// a plain ``approve``. Multi-turn chat refinement still works as a fallback.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
@ -110,7 +110,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
|
|||
// Per-automation model selection. The card always supplies models (chosen
|
||||
// here, not snapshotted from the search space), so Approve dispatches an
|
||||
// `edit` decision carrying `definition.models`.
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const eligibleModels = useAutomationEligibleModels();
|
||||
const [modelSelection, setModelSelection] = useState<AutomationModelSelection>({
|
||||
chatModelId: 0,
|
||||
|
|
@ -156,7 +156,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
|
|||
const plan = Array.isArray(baseDefinition.plan) ? baseDefinition.plan : [];
|
||||
const triggers = Array.isArray(baseArgs.triggers) ? baseArgs.triggers : [];
|
||||
trackAutomationChatApproved({
|
||||
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
edited: pendingEdits !== null,
|
||||
task_count: plan.length,
|
||||
trigger_type:
|
||||
|
|
@ -191,7 +191,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
|
|||
if (phase !== "pending" || !canReject || isEditing) return;
|
||||
setRejected();
|
||||
trackAutomationChatRejected({
|
||||
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
});
|
||||
onDecision({ type: "reject", message: "User rejected the automation draft." });
|
||||
}, [phase, canReject, isEditing, setRejected, onDecision, searchSpaceId]);
|
||||
|
|
@ -268,7 +268,7 @@ function ApprovalCard({ args, interruptData, onDecision }: ApprovalCardProps) {
|
|||
setPendingEdits(parsed);
|
||||
setIsEditing(false);
|
||||
trackAutomationChatDraftEdited({
|
||||
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
});
|
||||
}}
|
||||
onCancel={() => setIsEditing(false)}
|
||||
|
|
@ -385,7 +385,7 @@ function JsonEditor({ initialValue, onSave, onCancel }: JsonEditorProps) {
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
function SavedCard({ result }: { result: SavedResult }) {
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const tracked = useRef(false);
|
||||
useEffect(() => {
|
||||
if (tracked.current) return;
|
||||
|
|
@ -393,7 +393,7 @@ function SavedCard({ result }: { result: SavedResult }) {
|
|||
trackAutomationChatCreateSucceeded({
|
||||
automation_id: result.automation_id,
|
||||
name: result.name,
|
||||
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
});
|
||||
}, [result.automation_id, result.name, searchSpaceId]);
|
||||
|
||||
|
|
@ -429,7 +429,7 @@ function SavedCard({ result }: { result: SavedResult }) {
|
|||
}
|
||||
|
||||
function InvalidCard({ result }: { result: InvalidResult }) {
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const tracked = useRef(false);
|
||||
useEffect(() => {
|
||||
if (tracked.current) return;
|
||||
|
|
@ -437,7 +437,7 @@ function InvalidCard({ result }: { result: InvalidResult }) {
|
|||
trackAutomationChatCreateFailed({
|
||||
reason: "invalid",
|
||||
issue_count: result.issues.length,
|
||||
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
});
|
||||
}, [result.issues.length, searchSpaceId]);
|
||||
|
||||
|
|
@ -464,7 +464,7 @@ function InvalidCard({ result }: { result: InvalidResult }) {
|
|||
}
|
||||
|
||||
function ErrorCard({ result }: { result: ErrorResult }) {
|
||||
const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom);
|
||||
const searchSpaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const tracked = useRef(false);
|
||||
useEffect(() => {
|
||||
if (tracked.current) return;
|
||||
|
|
@ -472,7 +472,7 @@ function ErrorCard({ result }: { result: ErrorResult }) {
|
|||
trackAutomationChatCreateFailed({
|
||||
reason: "error",
|
||||
message: result.message,
|
||||
search_space_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
workspace_id: searchSpaceId ? Number(searchSpaceId) : undefined,
|
||||
});
|
||||
}, [result.message, searchSpaceId]);
|
||||
|
||||
|
|
@ -531,7 +531,7 @@ export const CreateAutomationToolUI = ({
|
|||
* Project raw args into the shape ``AutomationDraftPreview`` expects.
|
||||
*
|
||||
* The args dict is the full ``AutomationCreate`` payload (minus
|
||||
* ``search_space_id`` which is injected server-side), so we trust the
|
||||
* ``workspace_id`` which is injected server-side), so we trust the
|
||||
* top-level fields but defend against missing nested defaults.
|
||||
*/
|
||||
function extractDraft(args: Record<string, unknown>) {
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ External boundaries (LLM, embedding, chunking, Redis) are mocked in both suites.
|
|||
1. **Database setup** — `TEST_DATABASE_URL` defaults to `surfsense_test`. Tables and extensions (`vector`, `pg_trgm`) are created once per session and dropped after.
|
||||
2. **Transaction isolation** — Each test runs inside a savepoint that rolls back, so tests don't affect each other.
|
||||
3. **User creation** — Integration tests register a test user via `POST /auth/register` on first run, then log in for subsequent requests.
|
||||
4. **Search space discovery** — Tests call `GET /api/v1/searchspaces` and use the first available space.
|
||||
4. **Search space discovery** — Tests call `GET /api/v1/workspaces` and use the first available space.
|
||||
5. **Cleanup** — A session fixture purges stale documents before tests run. Per-test cleanup deletes documents via API, falling back to direct DB access for stuck records.
|
||||
|
||||
## Writing New Tests
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ export type Trigger = z.infer<typeof trigger>;
|
|||
// =============================================================================
|
||||
|
||||
export const automationCreateRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
name: z.string().min(1).max(200),
|
||||
description: z.string().nullable().optional(),
|
||||
definition: automationDefinition,
|
||||
|
|
@ -136,7 +136,7 @@ export type AutomationUpdateRequest = z.infer<typeof automationUpdateRequest>;
|
|||
|
||||
export const automationSummary = z.object({
|
||||
id: z.number(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
name: z.string(),
|
||||
description: z.string().nullable().optional(),
|
||||
status: automationStatus,
|
||||
|
|
@ -159,7 +159,7 @@ export const automationListResponse = z.object({
|
|||
export type AutomationListResponse = z.infer<typeof automationListResponse>;
|
||||
|
||||
export const automationListParams = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
limit: z.number().int().min(1).max(200).default(50),
|
||||
offset: z.number().int().min(0).default(0),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ export const mentionContext = z.object({
|
|||
thread_id: z.number(),
|
||||
thread_title: z.string(),
|
||||
message_id: z.number(),
|
||||
search_space_id: z.number(),
|
||||
search_space_name: z.string(),
|
||||
workspace_id: z.number(),
|
||||
workspace_name: z.string(),
|
||||
});
|
||||
|
||||
export const mentionComment = z.object({
|
||||
|
|
@ -144,7 +144,7 @@ export const deleteCommentResponse = z.object({
|
|||
* Get mentions
|
||||
*/
|
||||
export const getMentionsRequest = z.object({
|
||||
search_space_id: z.number().optional(),
|
||||
workspace_id: z.number().optional(),
|
||||
});
|
||||
|
||||
export const getMentionsResponse = z.object({
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export const publicChatSnapshotDetail = z.object({
|
|||
* List public chat snapshots for search space
|
||||
*/
|
||||
export const publicChatSnapshotsBySpaceRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
});
|
||||
|
||||
export const publicChatSnapshotsBySpaceResponse = z.object({
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export const searchSourceConnector = z.object({
|
|||
periodic_indexing_enabled: z.boolean(),
|
||||
indexing_frequency_minutes: z.number().nullable(),
|
||||
next_scheduled_at: z.string().nullable(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
user_id: z.string(),
|
||||
created_at: z.string(),
|
||||
});
|
||||
|
|
@ -72,7 +72,7 @@ export const getConnectorsRequest = z.object({
|
|||
queryParams: paginationQueryParams
|
||||
.pick({ skip: true, limit: true })
|
||||
.extend({
|
||||
search_space_id: z.number().or(z.string()).nullish(),
|
||||
workspace_id: z.number().or(z.string()).nullish(),
|
||||
})
|
||||
.nullish(),
|
||||
});
|
||||
|
|
@ -103,7 +103,7 @@ export const createConnectorRequest = z.object({
|
|||
next_scheduled_at: true,
|
||||
}),
|
||||
queryParams: z.object({
|
||||
search_space_id: z.number().or(z.string()),
|
||||
workspace_id: z.number().or(z.string()),
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -175,7 +175,7 @@ export const googleDriveIndexBody = z.object({
|
|||
export const indexConnectorRequest = z.object({
|
||||
connector_id: z.number(),
|
||||
queryParams: z.object({
|
||||
search_space_id: z.number().or(z.string()),
|
||||
workspace_id: z.number().or(z.string()),
|
||||
start_date: z.string().optional(),
|
||||
end_date: z.string().optional(),
|
||||
}),
|
||||
|
|
@ -185,7 +185,7 @@ export const indexConnectorRequest = z.object({
|
|||
export const indexConnectorResponse = z.object({
|
||||
message: z.string(),
|
||||
connector_id: z.number(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
indexing_from: z.string(),
|
||||
indexing_to: z.string(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export const document = z.object({
|
|||
unique_identifier_hash: z.string().nullable(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string().nullable(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
created_by_id: z.string().nullable().optional(),
|
||||
created_by_name: z.string().nullable().optional(),
|
||||
created_by_email: z.string().nullable().optional(),
|
||||
|
|
@ -85,7 +85,7 @@ export const sortOrderEnum = z.enum(["asc", "desc"]);
|
|||
export const getDocumentsRequest = z.object({
|
||||
queryParams: paginationQueryParams
|
||||
.extend({
|
||||
search_space_id: z.number().or(z.string()).optional(),
|
||||
workspace_id: z.number().or(z.string()).optional(),
|
||||
document_types: z.array(documentTypeEnum).optional(),
|
||||
sort_by: documentSortByEnum.optional(),
|
||||
sort_order: sortOrderEnum.optional(),
|
||||
|
|
@ -112,7 +112,7 @@ export const getDocumentResponse = document;
|
|||
* Create documents
|
||||
*/
|
||||
export const createDocumentRequest = document
|
||||
.pick({ document_type: true, search_space_id: true })
|
||||
.pick({ document_type: true, workspace_id: true })
|
||||
.extend({
|
||||
content: z.string().or(z.array(z.string())).or(z.array(extensionDocumentContent)),
|
||||
});
|
||||
|
|
@ -129,7 +129,7 @@ export const processingModeEnum = z.enum(["basic", "premium"]);
|
|||
|
||||
export const uploadDocumentRequest = z.object({
|
||||
files: z.array(z.instanceof(File)),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
use_vision_llm: z.boolean().default(false),
|
||||
processing_mode: processingModeEnum.default("basic"),
|
||||
});
|
||||
|
|
@ -148,7 +148,7 @@ export const uploadDocumentResponse = z.object({
|
|||
*/
|
||||
export const getDocumentsStatusRequest = z.object({
|
||||
queryParams: z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
document_ids: z.array(z.number()).min(1),
|
||||
}),
|
||||
});
|
||||
|
|
@ -175,7 +175,7 @@ export const getDocumentsStatusResponse = z.object({
|
|||
export const searchDocumentsRequest = z.object({
|
||||
queryParams: paginationQueryParams
|
||||
.extend({
|
||||
search_space_id: z.number().or(z.string()).optional(),
|
||||
workspace_id: z.number().or(z.string()).optional(),
|
||||
document_types: z.array(documentTypeEnum).optional(),
|
||||
title: z.string().optional(),
|
||||
})
|
||||
|
|
@ -200,18 +200,12 @@ export const documentTitleRead = z.object({
|
|||
});
|
||||
|
||||
export const searchDocumentTitlesRequest = z.object({
|
||||
queryParams: z
|
||||
.object({
|
||||
search_space_id: z.number().optional(),
|
||||
workspace_id: z.number().optional(),
|
||||
title: z.string().optional(),
|
||||
page: z.number().optional(),
|
||||
page_size: z.number().optional(),
|
||||
})
|
||||
.refine((params) => params.search_space_id !== undefined || params.workspace_id !== undefined, {
|
||||
message: "workspace_id is required",
|
||||
path: ["search_space_id"],
|
||||
}),
|
||||
queryParams: z.object({
|
||||
workspace_id: z.number(),
|
||||
title: z.string().optional(),
|
||||
page: z.number().optional(),
|
||||
page_size: z.number().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const searchDocumentTitlesResponse = z.object({
|
||||
|
|
@ -225,7 +219,7 @@ export const searchDocumentTitlesResponse = z.object({
|
|||
export const getDocumentTypeCountsRequest = z.object({
|
||||
queryParams: z
|
||||
.object({
|
||||
search_space_id: z.number().or(z.string()).optional(),
|
||||
workspace_id: z.number().or(z.string()).optional(),
|
||||
})
|
||||
.nullish(),
|
||||
});
|
||||
|
|
@ -272,7 +266,7 @@ export const getDocumentChunksResponse = z.object({
|
|||
*/
|
||||
export const updateDocumentRequest = z.object({
|
||||
id: z.number(),
|
||||
data: document.pick({ search_space_id: true, document_type: true, content: true }),
|
||||
data: document.pick({ workspace_id: true, document_type: true, content: true }),
|
||||
});
|
||||
|
||||
export const updateDocumentResponse = document;
|
||||
|
|
|
|||
|
|
@ -5,27 +5,19 @@ const folderBase = z.object({
|
|||
name: z.string(),
|
||||
position: z.string(),
|
||||
parent_id: z.number().nullable(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
created_by_id: z.string().nullable().optional(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
metadata: z.record(z.string(), z.any()).nullable().optional(),
|
||||
});
|
||||
|
||||
export const folder = z.preprocess((value) => {
|
||||
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
||||
const record = value as Record<string, unknown>;
|
||||
if (record.search_space_id === undefined && record.workspace_id !== undefined) {
|
||||
return { ...record, search_space_id: record.workspace_id };
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}, folderBase);
|
||||
export const folder = folderBase;
|
||||
|
||||
export const folderCreateRequest = z.object({
|
||||
name: z.string().min(1).max(255),
|
||||
parent_id: z.number().nullable().optional(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
});
|
||||
|
||||
export const folderUpdateRequest = z.object({
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { z } from "zod";
|
|||
export const imageGenerationListItem = z.object({
|
||||
id: z.number(),
|
||||
prompt: z.string(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
created_at: z.string(),
|
||||
is_success: z.boolean(),
|
||||
image_count: z.number().nullish(),
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ export const inboxItemMetadata = z.union([
|
|||
export const inboxItem = z.object({
|
||||
id: z.number(),
|
||||
user_id: z.string(),
|
||||
search_space_id: z.number().nullable(),
|
||||
workspace_id: z.number().nullable(),
|
||||
type: inboxItemTypeEnum,
|
||||
title: z.string(),
|
||||
message: z.string(),
|
||||
|
|
@ -210,7 +210,7 @@ export type NotificationCategory = z.infer<typeof notificationCategory>;
|
|||
*/
|
||||
export const getNotificationsRequest = z.object({
|
||||
queryParams: z.object({
|
||||
search_space_id: z.number().optional(),
|
||||
workspace_id: z.number().optional(),
|
||||
type: inboxItemTypeEnum.optional(),
|
||||
category: notificationCategory.optional(),
|
||||
source_type: z.string().optional(),
|
||||
|
|
@ -260,7 +260,7 @@ export const markAllNotificationsReadResponse = z.object({
|
|||
* Request schema for getting unread count
|
||||
*/
|
||||
export const getUnreadCountRequest = z.object({
|
||||
search_space_id: z.number().optional(),
|
||||
workspace_id: z.number().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export const invite = z.object({
|
|||
id: z.number(),
|
||||
name: z.string().max(100).nullable().optional(),
|
||||
invite_code: z.string(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
created_by_id: z.string().nullable(),
|
||||
role_id: z.number().nullable(),
|
||||
expires_at: z.string().nullable(),
|
||||
|
|
@ -20,7 +20,7 @@ export const invite = z.object({
|
|||
* Create invite
|
||||
*/
|
||||
export const createInviteRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
data: z.object({
|
||||
name: z.string().max(100).optional(),
|
||||
role_id: z.number().nullable().optional(),
|
||||
|
|
@ -35,7 +35,7 @@ export const createInviteResponse = invite;
|
|||
* Get invites
|
||||
*/
|
||||
export const getInvitesRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
});
|
||||
|
||||
export const getInvitesResponse = z.array(invite);
|
||||
|
|
@ -44,7 +44,7 @@ export const getInvitesResponse = z.array(invite);
|
|||
* Update invite
|
||||
*/
|
||||
export const updateInviteRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
invite_id: z.number(),
|
||||
data: z.object({
|
||||
name: z.string().max(100).optional(),
|
||||
|
|
@ -61,7 +61,7 @@ export const updateInviteResponse = invite;
|
|||
* Delete invite
|
||||
*/
|
||||
export const deleteInviteRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
invite_id: z.number(),
|
||||
});
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ export const getInviteInfoRequest = z.object({
|
|||
});
|
||||
|
||||
export const getInviteInfoResponse = z.object({
|
||||
search_space_name: z.string(),
|
||||
workspace_name: z.string(),
|
||||
role_name: z.string().nullable(),
|
||||
is_valid: z.boolean(),
|
||||
message: z.string().nullable(),
|
||||
|
|
@ -92,8 +92,8 @@ export const acceptInviteRequest = z.object({
|
|||
|
||||
export const acceptInviteResponse = z.object({
|
||||
message: z.string(),
|
||||
search_space_id: z.number(),
|
||||
search_space_name: z.string(),
|
||||
workspace_id: z.number(),
|
||||
workspace_name: z.string(),
|
||||
role_name: z.string().nullable(),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const log = z.object({
|
|||
source: z.string().nullable().optional(),
|
||||
log_metadata: z.record(z.string(), z.any()).nullable().optional(),
|
||||
created_at: z.string(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
});
|
||||
|
||||
export const logBase = log.omit({ id: true, created_at: true });
|
||||
|
|
@ -27,7 +27,7 @@ export const logBase = log.omit({ id: true, created_at: true });
|
|||
/**
|
||||
* Create log
|
||||
*/
|
||||
export const createLogRequest = logBase.extend({ search_space_id: z.number() });
|
||||
export const createLogRequest = logBase.extend({ workspace_id: z.number() });
|
||||
export const createLogResponse = log;
|
||||
|
||||
/**
|
||||
|
|
@ -48,7 +48,7 @@ export const deleteLogResponse = z.object({
|
|||
* Get logs (list)
|
||||
*/
|
||||
export const logFilters = z.object({
|
||||
search_space_id: z.number().optional(),
|
||||
workspace_id: z.number().optional(),
|
||||
level: logLevelEnum.optional(),
|
||||
status: logStatusEnum.optional(),
|
||||
source: z.string().optional(),
|
||||
|
|
@ -59,7 +59,7 @@ export const logFilters = z.object({
|
|||
export const getLogsRequest = z.object({
|
||||
queryParams: paginationQueryParams
|
||||
.extend({
|
||||
search_space_id: z.number().optional(),
|
||||
workspace_id: z.number().optional(),
|
||||
level: logLevelEnum.optional(),
|
||||
status: logStatusEnum.optional(),
|
||||
source: z.string().optional(),
|
||||
|
|
@ -106,7 +106,7 @@ export const logSummary = z.object({
|
|||
recent_failures: z.array(logFailure),
|
||||
});
|
||||
export const getLogSummaryRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
hours: z.number().optional(),
|
||||
});
|
||||
export const getLogSummaryResponse = logSummary;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export const mcpConnectorRead = z.object({
|
|||
name: z.string(),
|
||||
connector_type: z.literal("MCP_CONNECTOR"),
|
||||
server_config: mcpServerConfig,
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
user_id: z.string(),
|
||||
created_at: z.string(),
|
||||
updated_at: z.string(),
|
||||
|
|
@ -49,7 +49,7 @@ export const mcpConnectorRead = z.object({
|
|||
export const createMCPConnectorRequest = z.object({
|
||||
data: mcpConnectorCreate,
|
||||
queryParams: z.object({
|
||||
search_space_id: z.number().or(z.string()),
|
||||
workspace_id: z.number().or(z.string()),
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ export const updateMCPConnectorRequest = z.object({
|
|||
|
||||
export const getMCPConnectorsRequest = z.object({
|
||||
queryParams: z.object({
|
||||
search_space_id: z.number().or(z.string()),
|
||||
workspace_id: z.number().or(z.string()),
|
||||
}),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +1,27 @@
|
|||
import { z } from "zod";
|
||||
import { role } from "./roles.types";
|
||||
|
||||
export const membership = z.preprocess(
|
||||
(value) => {
|
||||
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
||||
const record = value as Record<string, unknown>;
|
||||
if (record.search_space_id === undefined && record.workspace_id !== undefined) {
|
||||
return { ...record, search_space_id: record.workspace_id };
|
||||
}
|
||||
}
|
||||
return value;
|
||||
},
|
||||
z.object({
|
||||
id: z.number(),
|
||||
user_id: z.string(),
|
||||
search_space_id: z.number(),
|
||||
role_id: z.number().nullable(),
|
||||
is_owner: z.boolean(),
|
||||
joined_at: z.string(),
|
||||
created_at: z.string(),
|
||||
role: role.nullable().optional(),
|
||||
user_email: z.string().nullable().optional(),
|
||||
user_display_name: z.string().nullable().optional(),
|
||||
user_avatar_url: z.string().nullable().optional(),
|
||||
user_last_login: z.string().nullable().optional(),
|
||||
user_is_active: z.boolean().nullable().optional(),
|
||||
})
|
||||
);
|
||||
export const membership = z.object({
|
||||
id: z.number(),
|
||||
user_id: z.string(),
|
||||
workspace_id: z.number(),
|
||||
role_id: z.number().nullable(),
|
||||
is_owner: z.boolean(),
|
||||
joined_at: z.string(),
|
||||
created_at: z.string(),
|
||||
role: role.nullable().optional(),
|
||||
user_email: z.string().nullable().optional(),
|
||||
user_display_name: z.string().nullable().optional(),
|
||||
user_avatar_url: z.string().nullable().optional(),
|
||||
user_last_login: z.string().nullable().optional(),
|
||||
user_is_active: z.boolean().nullable().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Get members
|
||||
*/
|
||||
export const getMembersRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
});
|
||||
|
||||
export const getMembersResponse = z.array(membership);
|
||||
|
|
@ -41,7 +30,7 @@ export const getMembersResponse = z.array(membership);
|
|||
* Update membership
|
||||
*/
|
||||
export const updateMembershipRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
membership_id: z.number(),
|
||||
data: z.object({
|
||||
role_id: z.number().nullable(),
|
||||
|
|
@ -54,7 +43,7 @@ export const updateMembershipResponse = membership;
|
|||
* Delete membership
|
||||
*/
|
||||
export const deleteMembershipRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
membership_id: z.number(),
|
||||
});
|
||||
|
||||
|
|
@ -63,10 +52,10 @@ export const deleteMembershipResponse = z.object({
|
|||
});
|
||||
|
||||
/**
|
||||
* Leave search space
|
||||
* Leave workspace
|
||||
*/
|
||||
export const leaveSearchSpaceRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
});
|
||||
|
||||
export const leaveSearchSpaceResponse = z.object({
|
||||
|
|
@ -77,22 +66,16 @@ export const leaveSearchSpaceResponse = z.object({
|
|||
* Get my access
|
||||
*/
|
||||
export const getMyAccessRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
});
|
||||
|
||||
export const getMyAccessResponse = z
|
||||
.object({
|
||||
workspace_name: z.string(),
|
||||
workspace_id: z.number(),
|
||||
is_owner: z.boolean(),
|
||||
permissions: z.array(z.string()),
|
||||
role_name: z.string().nullable(),
|
||||
})
|
||||
.transform(({ workspace_id, workspace_name, ...rest }) => ({
|
||||
...rest,
|
||||
search_space_id: workspace_id,
|
||||
search_space_name: workspace_name,
|
||||
}));
|
||||
export const getMyAccessResponse = z.object({
|
||||
workspace_name: z.string(),
|
||||
workspace_id: z.number(),
|
||||
is_owner: z.boolean(),
|
||||
permissions: z.array(z.string()),
|
||||
role_name: z.string().nullable(),
|
||||
});
|
||||
|
||||
export type Membership = z.infer<typeof membership>;
|
||||
export type GetMembersRequest = z.infer<typeof getMembersRequest>;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export const connectionRead = z.object({
|
|||
api_key: z.string().nullable().optional(),
|
||||
extra: z.record(z.string(), z.any()).default({}),
|
||||
scope: z.union([connectionScopeEnum, z.string()]),
|
||||
search_space_id: z.number().nullable().optional(),
|
||||
workspace_id: z.number().nullable().optional(),
|
||||
user_id: z.string().nullable().optional(),
|
||||
enabled: z.boolean(),
|
||||
has_api_key: z.boolean(),
|
||||
|
|
@ -57,7 +57,7 @@ export const connectionCreateRequest = z.object({
|
|||
api_key: z.string().nullable().optional(),
|
||||
extra: z.record(z.string(), z.any()).default({}),
|
||||
scope: connectionScopeEnum.default("SEARCH_SPACE"),
|
||||
search_space_id: z.number().nullable().optional(),
|
||||
workspace_id: z.number().nullable().optional(),
|
||||
enabled: z.boolean().default(true),
|
||||
models: z.array(modelSelection).default([]),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ export const podcastDetail = z.object({
|
|||
duration_seconds: z.number().nullable(),
|
||||
error: z.string().nullable(),
|
||||
created_at: z.string(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
thread_id: z.number().nullable(),
|
||||
});
|
||||
export type PodcastDetail = z.infer<typeof podcastDetail>;
|
||||
|
|
@ -162,7 +162,7 @@ export const podcastSummary = z.object({
|
|||
title: z.string(),
|
||||
status: podcastStatus,
|
||||
created_at: z.string(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
thread_id: z.number().nullish(),
|
||||
});
|
||||
export type PodcastSummary = z.infer<typeof podcastSummary>;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export const promptRead = z.object({
|
|||
name: z.string(),
|
||||
prompt: z.string(),
|
||||
mode: z.enum(["transform", "explore"]),
|
||||
search_space_id: z.number().nullable(),
|
||||
workspace_id: z.number().nullable(),
|
||||
is_public: z.boolean(),
|
||||
version: z.number(),
|
||||
created_at: z.string(),
|
||||
|
|
@ -29,7 +29,7 @@ export const promptCreateRequest = z.object({
|
|||
name: z.string().min(1).max(200),
|
||||
prompt: z.string().min(1),
|
||||
mode: z.enum(["transform", "explore"]),
|
||||
search_space_id: z.number().nullable().optional(),
|
||||
workspace_id: z.number().nullable().optional(),
|
||||
is_public: z.boolean().optional(),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export const clonePublicChatRequest = z.object({
|
|||
|
||||
export const clonePublicChatResponse = z.object({
|
||||
thread_id: z.number(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
});
|
||||
|
||||
// Type exports
|
||||
|
|
|
|||
|
|
@ -7,25 +7,17 @@ const roleBase = z.object({
|
|||
permissions: z.array(z.string()),
|
||||
is_default: z.boolean(),
|
||||
is_system_role: z.boolean(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
created_at: z.string(),
|
||||
});
|
||||
|
||||
export const role = z.preprocess((value) => {
|
||||
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
||||
const record = value as Record<string, unknown>;
|
||||
if (record.search_space_id === undefined && record.workspace_id !== undefined) {
|
||||
return { ...record, search_space_id: record.workspace_id };
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}, roleBase);
|
||||
export const role = roleBase;
|
||||
|
||||
/**
|
||||
* Create role
|
||||
*/
|
||||
export const createRoleRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
data: roleBase.pick({
|
||||
name: true,
|
||||
description: true,
|
||||
|
|
@ -40,7 +32,7 @@ export const createRoleResponse = role;
|
|||
* Get roles
|
||||
*/
|
||||
export const getRolesRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
});
|
||||
|
||||
export const getRolesResponse = z.array(role);
|
||||
|
|
@ -49,7 +41,7 @@ export const getRolesResponse = z.array(role);
|
|||
* Get role by ID
|
||||
*/
|
||||
export const getRoleByIdRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
role_id: z.number(),
|
||||
});
|
||||
|
||||
|
|
@ -59,7 +51,7 @@ export const getRoleByIdResponse = role;
|
|||
* Update role
|
||||
*/
|
||||
export const updateRoleRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
role_id: z.number(),
|
||||
data: roleBase
|
||||
.pick({
|
||||
|
|
@ -77,7 +69,7 @@ export const updateRoleResponse = role;
|
|||
* Delete role
|
||||
*/
|
||||
export const deleteRoleRequest = z.object({
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
role_id: z.number(),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export const purchaseStatusEnum = z.enum(["pending", "completed", "failed"]);
|
|||
|
||||
export const createCreditCheckoutSessionRequest = z.object({
|
||||
quantity: z.number().int().min(1).max(10_000),
|
||||
search_space_id: z.number().int().min(1),
|
||||
workspace_id: z.number().int().min(1),
|
||||
});
|
||||
|
||||
export const createCreditCheckoutSessionResponse = z.object({
|
||||
|
|
@ -90,7 +90,7 @@ export const updateAutoReloadSettingsRequest = z.object({
|
|||
});
|
||||
|
||||
export const createAutoReloadSetupSessionRequest = z.object({
|
||||
search_space_id: z.number().int().min(1),
|
||||
workspace_id: z.number().int().min(1),
|
||||
});
|
||||
|
||||
export const createAutoReloadSetupSessionResponse = z.object({
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export const videoPresentationListItem = z.object({
|
|||
title: z.string(),
|
||||
status: videoPresentationStatus.default("ready"),
|
||||
created_at: z.string(),
|
||||
search_space_id: z.number(),
|
||||
workspace_id: z.number(),
|
||||
thread_id: z.number().nullish(),
|
||||
});
|
||||
export type VideoPresentationListItem = z.infer<typeof videoPresentationListItem>;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue