mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-14 22:52:15 +02:00
parent
c96be7d9e1
commit
d9e6947fbd
18 changed files with 19 additions and 506 deletions
|
|
@ -1,10 +1,5 @@
|
||||||
NEXT_PUBLIC_FASTAPI_BACKEND_URL=http://localhost:8000
|
NEXT_PUBLIC_FASTAPI_BACKEND_URL=http://localhost:8000
|
||||||
NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE=LOCAL or GOOGLE
|
NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE=LOCAL or GOOGLE
|
||||||
NEXT_PUBLIC_ETL_SERVICE=UNSTRUCTURED or LLAMACLOUD or DOCLING
|
NEXT_PUBLIC_ETL_SERVICE=UNSTRUCTURED or LLAMACLOUD or DOCLING
|
||||||
|
|
||||||
# Enable Posthog - OPTIONAL
|
|
||||||
NEXT_PUBLIC_POSTHOG_KEY=phc_XcAf95z8Vl
|
|
||||||
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
|
|
||||||
|
|
||||||
# Contact Form Vars - OPTIONAL
|
# Contact Form Vars - OPTIONAL
|
||||||
DATABASE_URL=postgresql://postgres:[YOUR-PASSWORD]@db.sdsf.supabase.co:5432/postgres
|
DATABASE_URL=postgresql://postgres:[YOUR-PASSWORD]@db.sdsf.supabase.co:5432/postgres
|
||||||
|
|
@ -9,7 +9,6 @@ import { useEffect, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { registerMutationAtom } from "@/atoms/auth/auth-mutation.atoms";
|
import { registerMutationAtom } from "@/atoms/auth/auth-mutation.atoms";
|
||||||
import { Logo } from "@/components/Logo";
|
import { Logo } from "@/components/Logo";
|
||||||
import { trackUserSignedUp } from "@/lib/analytics";
|
|
||||||
import { getAuthErrorDetails, isNetworkError, shouldRetry } from "@/lib/auth-errors";
|
import { getAuthErrorDetails, isNetworkError, shouldRetry } from "@/lib/auth-errors";
|
||||||
import { AppError, ValidationError } from "@/lib/error";
|
import { AppError, ValidationError } from "@/lib/error";
|
||||||
import { AmbientBackground } from "../login/AmbientBackground";
|
import { AmbientBackground } from "../login/AmbientBackground";
|
||||||
|
|
@ -65,9 +64,6 @@ export default function RegisterPage() {
|
||||||
is_verified: false,
|
is_verified: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Track successful registration
|
|
||||||
trackUserSignedUp({ method: "email" });
|
|
||||||
|
|
||||||
// Success toast
|
// Success toast
|
||||||
toast.success(t("register_success"), {
|
toast.success(t("register_success"), {
|
||||||
id: loadingToast,
|
id: loadingToast,
|
||||||
|
|
|
||||||
|
|
@ -359,14 +359,10 @@ export default function NewChatPage() {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: message.content;
|
: message.content;
|
||||||
appendMessage(
|
appendMessage(currentThreadId, {
|
||||||
currentThreadId,
|
role: "user",
|
||||||
{
|
content: persistContent,
|
||||||
role: "user",
|
}).catch((err) => console.error("Failed to persist user message:", err));
|
||||||
content: persistContent,
|
|
||||||
},
|
|
||||||
searchSpaceId
|
|
||||||
).catch((err) => console.error("Failed to persist user message:", err));
|
|
||||||
|
|
||||||
// Start streaming response
|
// Start streaming response
|
||||||
setIsRunning(true);
|
setIsRunning(true);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { GoogleAnalytics } from "@next/third-parties/google";
|
||||||
import { RootProvider } from "fumadocs-ui/provider/next";
|
import { RootProvider } from "fumadocs-ui/provider/next";
|
||||||
import { Roboto } from "next/font/google";
|
import { Roboto } from "next/font/google";
|
||||||
import { I18nProvider } from "@/components/providers/I18nProvider";
|
import { I18nProvider } from "@/components/providers/I18nProvider";
|
||||||
import { PostHogProvider } from "@/components/providers/PostHogProvider";
|
|
||||||
import { ThemeProvider } from "@/components/theme/theme-provider";
|
import { ThemeProvider } from "@/components/theme/theme-provider";
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
import { LocaleProvider } from "@/contexts/LocaleContext";
|
import { LocaleProvider } from "@/contexts/LocaleContext";
|
||||||
|
|
@ -103,9 +102,7 @@ export default function RootLayout({
|
||||||
defaultTheme="light"
|
defaultTheme="light"
|
||||||
>
|
>
|
||||||
<RootProvider>
|
<RootProvider>
|
||||||
<ReactQueryClientProvider>
|
<ReactQueryClientProvider>{children}</ReactQueryClientProvider>
|
||||||
<PostHogProvider>{children}</PostHogProvider>
|
|
||||||
</ReactQueryClientProvider>
|
|
||||||
<Toaster />
|
<Toaster />
|
||||||
</RootProvider>
|
</RootProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import type {
|
||||||
UpdateDocumentRequest,
|
UpdateDocumentRequest,
|
||||||
UploadDocumentRequest,
|
UploadDocumentRequest,
|
||||||
} from "@/contracts/types/document.types";
|
} from "@/contracts/types/document.types";
|
||||||
import { trackDocumentDeleted, trackDocumentIndexed } from "@/lib/analytics";
|
|
||||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||||
import { queryClient } from "@/lib/query-client/client";
|
import { queryClient } from "@/lib/query-client/client";
|
||||||
|
|
@ -25,15 +24,7 @@ export const createDocumentMutationAtom = atomWithMutation((get) => {
|
||||||
return documentsApiService.createDocument(request);
|
return documentsApiService.createDocument(request);
|
||||||
},
|
},
|
||||||
|
|
||||||
onSuccess: (data, request: CreateDocumentRequest) => {
|
onSuccess: () => {
|
||||||
// Track document creation/indexing
|
|
||||||
if (searchSpaceId) {
|
|
||||||
trackDocumentIndexed({
|
|
||||||
search_space_id: Number(searchSpaceId),
|
|
||||||
document_type: request.document_type,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success("Document created successfully");
|
toast.success("Document created successfully");
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: cacheKeys.documents.globalQueryParams(documentsQueryParams),
|
queryKey: cacheKeys.documents.globalQueryParams(documentsQueryParams),
|
||||||
|
|
@ -100,14 +91,6 @@ export const deleteDocumentMutationAtom = atomWithMutation((get) => {
|
||||||
},
|
},
|
||||||
|
|
||||||
onSuccess: (_, request: DeleteDocumentRequest) => {
|
onSuccess: (_, request: DeleteDocumentRequest) => {
|
||||||
// Track document deletion
|
|
||||||
if (searchSpaceId) {
|
|
||||||
trackDocumentDeleted({
|
|
||||||
search_space_id: Number(searchSpaceId),
|
|
||||||
document_id: request.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success("Document deleted successfully");
|
toast.success("Document deleted successfully");
|
||||||
queryClient.setQueryData(
|
queryClient.setQueryData(
|
||||||
cacheKeys.documents.globalQueryParams(documentsQueryParams),
|
cacheKeys.documents.globalQueryParams(documentsQueryParams),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import type {
|
||||||
DeleteInviteRequest,
|
DeleteInviteRequest,
|
||||||
UpdateInviteRequest,
|
UpdateInviteRequest,
|
||||||
} from "@/contracts/types/invites.types";
|
} from "@/contracts/types/invites.types";
|
||||||
import { trackInviteAccepted, trackInviteCreated } from "@/lib/analytics";
|
|
||||||
import { invitesApiService } from "@/lib/apis/invites-api.service";
|
import { invitesApiService } from "@/lib/apis/invites-api.service";
|
||||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||||
import { queryClient } from "@/lib/query-client/client";
|
import { queryClient } from "@/lib/query-client/client";
|
||||||
|
|
@ -19,12 +18,6 @@ export const createInviteMutationAtom = atomWithMutation(() => ({
|
||||||
return invitesApiService.createInvite(request);
|
return invitesApiService.createInvite(request);
|
||||||
},
|
},
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
// Track invite creation
|
|
||||||
trackInviteCreated({
|
|
||||||
search_space_id: variables.search_space_id,
|
|
||||||
role_name: variables.data.role_id ? `role_${variables.data.role_id}` : undefined,
|
|
||||||
});
|
|
||||||
|
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: cacheKeys.invites.all(variables.search_space_id.toString()),
|
queryKey: cacheKeys.invites.all(variables.search_space_id.toString()),
|
||||||
});
|
});
|
||||||
|
|
@ -81,13 +74,7 @@ export const acceptInviteMutationAtom = atomWithMutation(() => ({
|
||||||
mutationFn: async (request: AcceptInviteRequest) => {
|
mutationFn: async (request: AcceptInviteRequest) => {
|
||||||
return invitesApiService.acceptInvite(request);
|
return invitesApiService.acceptInvite(request);
|
||||||
},
|
},
|
||||||
onSuccess: (data, variables) => {
|
onSuccess: () => {
|
||||||
// Track invite acceptance
|
|
||||||
trackInviteAccepted({
|
|
||||||
search_space_id: data.search_space_id,
|
|
||||||
invite_code: variables.invite_code,
|
|
||||||
});
|
|
||||||
|
|
||||||
queryClient.invalidateQueries({ queryKey: cacheKeys.searchSpaces.all });
|
queryClient.invalidateQueries({ queryKey: cacheKeys.searchSpaces.all });
|
||||||
toast.success("Invite accepted successfully");
|
toast.success("Invite accepted successfully");
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import type {
|
||||||
UpdateNewLLMConfigRequest,
|
UpdateNewLLMConfigRequest,
|
||||||
UpdateNewLLMConfigResponse,
|
UpdateNewLLMConfigResponse,
|
||||||
} from "@/contracts/types/new-llm-config.types";
|
} from "@/contracts/types/new-llm-config.types";
|
||||||
import { trackLLMConfigCreated } from "@/lib/analytics";
|
|
||||||
import { newLLMConfigApiService } from "@/lib/apis/new-llm-config-api.service";
|
import { newLLMConfigApiService } from "@/lib/apis/new-llm-config-api.service";
|
||||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||||
import { queryClient } from "@/lib/query-client/client";
|
import { queryClient } from "@/lib/query-client/client";
|
||||||
|
|
@ -26,16 +25,7 @@ export const createNewLLMConfigMutationAtom = atomWithMutation((get) => {
|
||||||
mutationFn: async (request: CreateNewLLMConfigRequest) => {
|
mutationFn: async (request: CreateNewLLMConfigRequest) => {
|
||||||
return newLLMConfigApiService.createConfig(request);
|
return newLLMConfigApiService.createConfig(request);
|
||||||
},
|
},
|
||||||
onSuccess: (_, request: CreateNewLLMConfigRequest) => {
|
onSuccess: () => {
|
||||||
// Track LLM config creation
|
|
||||||
if (searchSpaceId) {
|
|
||||||
trackLLMConfigCreated({
|
|
||||||
search_space_id: Number(searchSpaceId),
|
|
||||||
provider: request.provider,
|
|
||||||
model_name: request.model_name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success("Configuration created successfully");
|
toast.success("Configuration created successfully");
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: cacheKeys.newLLMConfigs.all(Number(searchSpaceId)),
|
queryKey: cacheKeys.newLLMConfigs.all(Number(searchSpaceId)),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import type {
|
||||||
DeleteSearchSpaceRequest,
|
DeleteSearchSpaceRequest,
|
||||||
UpdateSearchSpaceRequest,
|
UpdateSearchSpaceRequest,
|
||||||
} from "@/contracts/types/search-space.types";
|
} from "@/contracts/types/search-space.types";
|
||||||
import { trackSearchSpaceCreated, trackSearchSpaceDeleted } from "@/lib/analytics";
|
|
||||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||||
import { queryClient } from "@/lib/query-client/client";
|
import { queryClient } from "@/lib/query-client/client";
|
||||||
|
|
@ -18,10 +17,7 @@ export const createSearchSpaceMutationAtom = atomWithMutation(() => {
|
||||||
return searchSpacesApiService.createSearchSpace(request);
|
return searchSpacesApiService.createSearchSpace(request);
|
||||||
},
|
},
|
||||||
|
|
||||||
onSuccess: (data, request: CreateSearchSpaceRequest) => {
|
onSuccess: () => {
|
||||||
// Track search space creation
|
|
||||||
trackSearchSpaceCreated({ search_space_id: data.id, name: request.name });
|
|
||||||
|
|
||||||
toast.success("Search space created successfully");
|
toast.success("Search space created successfully");
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: cacheKeys.searchSpaces.all,
|
queryKey: cacheKeys.searchSpaces.all,
|
||||||
|
|
@ -65,11 +61,6 @@ export const deleteSearchSpaceMutationAtom = atomWithMutation((get) => {
|
||||||
},
|
},
|
||||||
|
|
||||||
onSuccess: (_, request: DeleteSearchSpaceRequest) => {
|
onSuccess: (_, request: DeleteSearchSpaceRequest) => {
|
||||||
// Track search space deletion
|
|
||||||
if (request.id) {
|
|
||||||
trackSearchSpaceDeleted({ search_space_id: request.id });
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success("Search space deleted successfully");
|
toast.success("Search space deleted successfully");
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: cacheKeys.searchSpaces.all,
|
queryKey: cacheKeys.searchSpaces.all,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { trackUserLoggedIn } from "@/lib/analytics";
|
|
||||||
import { getAndClearRedirectPath, setBearerToken } from "@/lib/auth-utils";
|
import { getAndClearRedirectPath, setBearerToken } from "@/lib/auth-utils";
|
||||||
|
|
||||||
interface TokenHandlerProps {
|
interface TokenHandlerProps {
|
||||||
|
|
@ -41,10 +40,6 @@ const TokenHandler = ({
|
||||||
localStorage.setItem(storageKey, token);
|
localStorage.setItem(storageKey, token);
|
||||||
setBearerToken(token);
|
setBearerToken(token);
|
||||||
|
|
||||||
// Track successful login (works for both email and Google OAuth)
|
|
||||||
const authType = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE || "GOOGLE";
|
|
||||||
trackUserLoggedIn({ method: authType === "GOOGLE" ? "google" : "email" });
|
|
||||||
|
|
||||||
// Check if there's a saved redirect path from before the auth flow
|
// Check if there's a saved redirect path from before the auth flow
|
||||||
const savedRedirectPath = getAndClearRedirectPath();
|
const savedRedirectPath = getAndClearRedirectPath();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { useAtomValue } from "jotai";
|
|
||||||
import posthog from "posthog-js";
|
|
||||||
import { PostHogProvider as PHProvider, usePostHog } from "posthog-js/react";
|
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
import { currentUserAtom } from "@/atoms/user/user-query.atoms";
|
|
||||||
|
|
||||||
// Initialize PostHog only on client side
|
|
||||||
if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
|
|
||||||
posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY, {
|
|
||||||
api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || "https://us.i.posthog.com",
|
|
||||||
person_profiles: "identified_only",
|
|
||||||
capture_pageview: true,
|
|
||||||
capture_pageleave: true,
|
|
||||||
autocapture: false, // We'll use manual event tracking for better control
|
|
||||||
persistence: "localStorage",
|
|
||||||
loaded: (posthog) => {
|
|
||||||
if (process.env.NODE_ENV === "development") {
|
|
||||||
// Uncomment to debug in development
|
|
||||||
// posthog.debug();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component that handles user identification with PostHog
|
|
||||||
* Placed inside the provider hierarchy to access user data
|
|
||||||
*/
|
|
||||||
function PostHogUserIdentifier() {
|
|
||||||
const ph = usePostHog();
|
|
||||||
const { data: user, isSuccess } = useAtomValue(currentUserAtom);
|
|
||||||
const hasIdentified = useRef(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isSuccess && user && !hasIdentified.current) {
|
|
||||||
// Identify the user with PostHog
|
|
||||||
ph.identify(user.id, {
|
|
||||||
email: user.email,
|
|
||||||
is_active: user.is_active,
|
|
||||||
is_superuser: user.is_superuser,
|
|
||||||
is_verified: user.is_verified,
|
|
||||||
});
|
|
||||||
hasIdentified.current = true;
|
|
||||||
}
|
|
||||||
}, [ph, user, isSuccess]);
|
|
||||||
|
|
||||||
// Reset identification flag when user logs out (user becomes null)
|
|
||||||
useEffect(() => {
|
|
||||||
if (!user && hasIdentified.current) {
|
|
||||||
ph.reset();
|
|
||||||
hasIdentified.current = false;
|
|
||||||
}
|
|
||||||
}, [ph, user]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PostHog Analytics Provider
|
|
||||||
* Wraps the app to enable analytics tracking and user identification
|
|
||||||
*/
|
|
||||||
export function PostHogProvider({ children }: { children: React.ReactNode }) {
|
|
||||||
// Don't render provider if PostHog key is not configured
|
|
||||||
if (!process.env.NEXT_PUBLIC_POSTHOG_KEY) {
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PHProvider client={posthog}>
|
|
||||||
<PostHogUserIdentifier />
|
|
||||||
{children}
|
|
||||||
</PHProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -146,7 +146,7 @@ export function AppSidebarProvider({
|
||||||
|
|
||||||
setIsDeletingThread(true);
|
setIsDeletingThread(true);
|
||||||
try {
|
try {
|
||||||
await deleteThread(threadToDelete.id, searchSpaceId);
|
await deleteThread(threadToDelete.id);
|
||||||
// Invalidate threads query to refresh the list
|
// Invalidate threads query to refresh the list
|
||||||
queryClient.invalidateQueries({ queryKey: ["threads", searchSpaceId] });
|
queryClient.invalidateQueries({ queryKey: ["threads", searchSpaceId] });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ export function AllChatsSidebar({ open, onOpenChange, searchSpaceId }: AllChatsS
|
||||||
async (threadId: number) => {
|
async (threadId: number) => {
|
||||||
setDeletingThreadId(threadId);
|
setDeletingThreadId(threadId);
|
||||||
try {
|
try {
|
||||||
await deleteThread(threadId, searchSpaceId);
|
await deleteThread(threadId);
|
||||||
toast.success(t("chat_deleted") || "Chat deleted successfully");
|
toast.success(t("chat_deleted") || "Chat deleted successfully");
|
||||||
queryClient.invalidateQueries({ queryKey: ["all-threads", searchSpaceId] });
|
queryClient.invalidateQueries({ queryKey: ["all-threads", searchSpaceId] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["search-threads", searchSpaceId] });
|
queryClient.invalidateQueries({ queryKey: ["search-threads", searchSpaceId] });
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,9 @@
|
||||||
|
|
||||||
import { makeAssistantToolUI } from "@assistant-ui/react";
|
import { makeAssistantToolUI } from "@assistant-ui/react";
|
||||||
import { AlertCircleIcon, Loader2Icon, MicIcon } from "lucide-react";
|
import { AlertCircleIcon, Loader2Icon, MicIcon } from "lucide-react";
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { Audio } from "@/components/tool-ui/audio";
|
import { Audio } from "@/components/tool-ui/audio";
|
||||||
import { trackPodcastGenerated } from "@/lib/analytics";
|
|
||||||
import { baseApiService } from "@/lib/apis/base-api.service";
|
import { baseApiService } from "@/lib/apis/base-api.service";
|
||||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||||
import { clearActivePodcastTaskId, setActivePodcastTaskId } from "@/lib/chat/podcast-state";
|
import { clearActivePodcastTaskId, setActivePodcastTaskId } from "@/lib/chat/podcast-state";
|
||||||
|
|
@ -289,9 +287,6 @@ function PodcastPlayer({
|
||||||
function PodcastTaskPoller({ taskId, title }: { taskId: string; title: string }) {
|
function PodcastTaskPoller({ taskId, title }: { taskId: string; title: string }) {
|
||||||
const [taskStatus, setTaskStatus] = useState<TaskStatusResponse>({ status: "processing" });
|
const [taskStatus, setTaskStatus] = useState<TaskStatusResponse>({ status: "processing" });
|
||||||
const pollingRef = useRef<NodeJS.Timeout | null>(null);
|
const pollingRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
const hasTrackedRef = useRef(false);
|
|
||||||
const params = useParams();
|
|
||||||
const searchSpaceId = params.search_space_id ? Number(params.search_space_id) : undefined;
|
|
||||||
|
|
||||||
// Set active podcast state when this component mounts
|
// Set active podcast state when this component mounts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -322,20 +317,6 @@ function PodcastTaskPoller({ taskId, title }: { taskId: string; title: string })
|
||||||
}
|
}
|
||||||
// Clear the active podcast state when task completes
|
// Clear the active podcast state when task completes
|
||||||
clearActivePodcastTaskId();
|
clearActivePodcastTaskId();
|
||||||
|
|
||||||
// Track successful podcast generation (only once)
|
|
||||||
if (
|
|
||||||
response.status === "success" &&
|
|
||||||
response.podcast_id &&
|
|
||||||
searchSpaceId &&
|
|
||||||
!hasTrackedRef.current
|
|
||||||
) {
|
|
||||||
hasTrackedRef.current = true;
|
|
||||||
trackPodcastGenerated({
|
|
||||||
search_space_id: searchSpaceId,
|
|
||||||
podcast_id: response.podcast_id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error polling task status:", err);
|
console.error("Error polling task status:", err);
|
||||||
|
|
@ -354,7 +335,7 @@ function PodcastTaskPoller({ taskId, title }: { taskId: string; title: string })
|
||||||
clearInterval(pollingRef.current);
|
clearInterval(pollingRef.current);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [taskId, searchSpaceId]);
|
}, [taskId]);
|
||||||
|
|
||||||
// Show loading state while processing
|
// Show loading state while processing
|
||||||
if (taskStatus.status === "processing") {
|
if (taskStatus.status === "processing") {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { trackConnectorAdded, trackConnectorDeleted, trackConnectorIndexed } from "@/lib/analytics";
|
|
||||||
import { authenticatedFetch, getBearerToken, handleUnauthorized } from "@/lib/auth-utils";
|
import { authenticatedFetch, getBearerToken, handleUnauthorized } from "@/lib/auth-utils";
|
||||||
|
|
||||||
export interface SearchSourceConnector {
|
export interface SearchSourceConnector {
|
||||||
|
|
@ -192,14 +191,6 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?:
|
||||||
const updatedConnectors = [...connectors, newConnector];
|
const updatedConnectors = [...connectors, newConnector];
|
||||||
setConnectors(updatedConnectors);
|
setConnectors(updatedConnectors);
|
||||||
updateConnectorSourceItems(updatedConnectors);
|
updateConnectorSourceItems(updatedConnectors);
|
||||||
|
|
||||||
// Track connector creation
|
|
||||||
trackConnectorAdded({
|
|
||||||
search_space_id: spaceId,
|
|
||||||
connector_type: connectorData.connector_type,
|
|
||||||
connector_id: newConnector.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return newConnector;
|
return newConnector;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error creating search source connector:", err);
|
console.error("Error creating search source connector:", err);
|
||||||
|
|
@ -248,9 +239,6 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?:
|
||||||
*/
|
*/
|
||||||
const deleteConnector = async (connectorId: number) => {
|
const deleteConnector = async (connectorId: number) => {
|
||||||
try {
|
try {
|
||||||
// Find connector before deleting to get its type for tracking
|
|
||||||
const connectorToDelete = connectors.find((c) => c.id === connectorId);
|
|
||||||
|
|
||||||
const response = await authenticatedFetch(
|
const response = await authenticatedFetch(
|
||||||
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-source-connectors/${connectorId}`,
|
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/search-source-connectors/${connectorId}`,
|
||||||
{
|
{
|
||||||
|
|
@ -266,15 +254,6 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?:
|
||||||
const updatedConnectors = connectors.filter((connector) => connector.id !== connectorId);
|
const updatedConnectors = connectors.filter((connector) => connector.id !== connectorId);
|
||||||
setConnectors(updatedConnectors);
|
setConnectors(updatedConnectors);
|
||||||
updateConnectorSourceItems(updatedConnectors);
|
updateConnectorSourceItems(updatedConnectors);
|
||||||
|
|
||||||
// Track connector deletion
|
|
||||||
if (connectorToDelete) {
|
|
||||||
trackConnectorDeleted({
|
|
||||||
search_space_id: connectorToDelete.search_space_id,
|
|
||||||
connector_type: connectorToDelete.connector_type,
|
|
||||||
connector_id: connectorId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error deleting search source connector:", err);
|
console.error("Error deleting search source connector:", err);
|
||||||
throw err;
|
throw err;
|
||||||
|
|
@ -318,9 +297,6 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?:
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
// Find the connector to get its type for tracking
|
|
||||||
const indexedConnector = connectors.find((c) => c.id === connectorId);
|
|
||||||
|
|
||||||
// Update the connector's last_indexed_at timestamp
|
// Update the connector's last_indexed_at timestamp
|
||||||
const updatedConnectors = connectors.map((connector) =>
|
const updatedConnectors = connectors.map((connector) =>
|
||||||
connector.id === connectorId
|
connector.id === connectorId
|
||||||
|
|
@ -332,15 +308,6 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?:
|
||||||
);
|
);
|
||||||
setConnectors(updatedConnectors);
|
setConnectors(updatedConnectors);
|
||||||
|
|
||||||
// Track connector indexing
|
|
||||||
if (indexedConnector) {
|
|
||||||
trackConnectorIndexed({
|
|
||||||
search_space_id: Number(searchSpaceId),
|
|
||||||
connector_type: indexedConnector.connector_type,
|
|
||||||
connector_id: connectorId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Error indexing connector content:", err);
|
console.error("Error indexing connector content:", err);
|
||||||
|
|
|
||||||
|
|
@ -1,220 +0,0 @@
|
||||||
/**
|
|
||||||
* PostHog Analytics Utility
|
|
||||||
* Provides typed event tracking functions for SurfSense
|
|
||||||
*/
|
|
||||||
|
|
||||||
import posthog from "posthog-js";
|
|
||||||
|
|
||||||
// Check if PostHog is initialized
|
|
||||||
function isPostHogReady(): boolean {
|
|
||||||
return typeof window !== "undefined" && !!process.env.NEXT_PUBLIC_POSTHOG_KEY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Event Names (Constants for consistency)
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export const ANALYTICS_EVENTS = {
|
|
||||||
// Authentication
|
|
||||||
USER_SIGNED_UP: "user_signed_up",
|
|
||||||
USER_LOGGED_IN: "user_logged_in",
|
|
||||||
USER_LOGGED_OUT: "user_logged_out",
|
|
||||||
|
|
||||||
// Search Spaces
|
|
||||||
SEARCH_SPACE_CREATED: "search_space_created",
|
|
||||||
SEARCH_SPACE_DELETED: "search_space_deleted",
|
|
||||||
|
|
||||||
// Chat
|
|
||||||
CHAT_CREATED: "chat_created",
|
|
||||||
MESSAGE_SENT: "message_sent",
|
|
||||||
CHAT_DELETED: "chat_deleted",
|
|
||||||
|
|
||||||
// Documents
|
|
||||||
DOCUMENT_INDEXED: "document_indexed",
|
|
||||||
DOCUMENT_DELETED: "document_deleted",
|
|
||||||
|
|
||||||
// Connectors
|
|
||||||
CONNECTOR_ADDED: "connector_added",
|
|
||||||
CONNECTOR_DELETED: "connector_deleted",
|
|
||||||
CONNECTOR_INDEXED: "connector_indexed",
|
|
||||||
|
|
||||||
// Podcasts
|
|
||||||
PODCAST_GENERATED: "podcast_generated",
|
|
||||||
PODCAST_DELETED: "podcast_deleted",
|
|
||||||
|
|
||||||
// LLM Config
|
|
||||||
LLM_CONFIG_CREATED: "llm_config_created",
|
|
||||||
|
|
||||||
// Invites
|
|
||||||
INVITE_CREATED: "invite_created",
|
|
||||||
INVITE_ACCEPTED: "invite_accepted",
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Authentication Events
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export function trackUserSignedUp(properties?: { method?: "email" | "google" }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.USER_SIGNED_UP, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackUserLoggedIn(properties?: { method?: "email" | "google" }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.USER_LOGGED_IN, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackUserLoggedOut() {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.USER_LOGGED_OUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Search Space Events
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export function trackSearchSpaceCreated(properties: { search_space_id: number; name: string }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.SEARCH_SPACE_CREATED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackSearchSpaceDeleted(properties: { search_space_id: number }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.SEARCH_SPACE_DELETED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Chat Events
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export function trackChatCreated(properties: { search_space_id: number; thread_id: number }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.CHAT_CREATED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackMessageSent(properties: {
|
|
||||||
search_space_id: number;
|
|
||||||
thread_id: number;
|
|
||||||
role: "user" | "assistant" | "system";
|
|
||||||
}) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.MESSAGE_SENT, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackChatDeleted(properties: { search_space_id: number; thread_id: number }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.CHAT_DELETED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Document Events
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export function trackDocumentIndexed(properties: {
|
|
||||||
search_space_id: number;
|
|
||||||
document_type: string;
|
|
||||||
count?: number;
|
|
||||||
}) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.DOCUMENT_INDEXED, {
|
|
||||||
...properties,
|
|
||||||
count: properties.count ?? 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackDocumentDeleted(properties: { search_space_id: number; document_id: number }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.DOCUMENT_DELETED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Connector Events
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export function trackConnectorAdded(properties: {
|
|
||||||
search_space_id: number;
|
|
||||||
connector_type: string;
|
|
||||||
connector_id?: number;
|
|
||||||
}) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.CONNECTOR_ADDED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackConnectorDeleted(properties: {
|
|
||||||
search_space_id: number;
|
|
||||||
connector_type: string;
|
|
||||||
connector_id: number;
|
|
||||||
}) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.CONNECTOR_DELETED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackConnectorIndexed(properties: {
|
|
||||||
search_space_id: number;
|
|
||||||
connector_type: string;
|
|
||||||
connector_id: number;
|
|
||||||
}) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.CONNECTOR_INDEXED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Podcast Events
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export function trackPodcastGenerated(properties: { search_space_id: number; podcast_id: number }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.PODCAST_GENERATED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackPodcastDeleted(properties: { search_space_id: number; podcast_id: number }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.PODCAST_DELETED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// LLM Config Events
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export function trackLLMConfigCreated(properties: {
|
|
||||||
search_space_id: number;
|
|
||||||
provider: string;
|
|
||||||
model_name: string;
|
|
||||||
}) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.LLM_CONFIG_CREATED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Invite Events
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
export function trackInviteCreated(properties: { search_space_id: number; role_name?: string }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.INVITE_CREATED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function trackInviteAccepted(properties: { search_space_id: number; invite_code: string }) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(ANALYTICS_EVENTS.INVITE_ACCEPTED, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Utility Functions
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic event capture for custom events
|
|
||||||
*/
|
|
||||||
export function trackEvent(eventName: string, properties?: Record<string, unknown>) {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.capture(eventName, properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset PostHog user identification (call on logout)
|
|
||||||
*/
|
|
||||||
export function resetAnalytics() {
|
|
||||||
if (!isPostHogReady()) return;
|
|
||||||
posthog.reset();
|
|
||||||
}
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
* Provides API functions and thread list management.
|
* Provides API functions and thread list management.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { trackChatCreated, trackChatDeleted, trackMessageSent } from "@/lib/analytics";
|
|
||||||
import { baseApiService } from "@/lib/apis/base-api.service";
|
import { baseApiService } from "@/lib/apis/base-api.service";
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
@ -81,18 +80,13 @@ export async function createThread(
|
||||||
searchSpaceId: number,
|
searchSpaceId: number,
|
||||||
title = "New Chat"
|
title = "New Chat"
|
||||||
): Promise<ThreadRecord> {
|
): Promise<ThreadRecord> {
|
||||||
const thread = await baseApiService.post<ThreadRecord>("/api/v1/threads", undefined, {
|
return baseApiService.post<ThreadRecord>("/api/v1/threads", undefined, {
|
||||||
body: {
|
body: {
|
||||||
title,
|
title,
|
||||||
archived: false,
|
archived: false,
|
||||||
search_space_id: searchSpaceId,
|
search_space_id: searchSpaceId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Track chat creation event
|
|
||||||
trackChatCreated({ search_space_id: searchSpaceId, thread_id: thread.id });
|
|
||||||
|
|
||||||
return thread;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -107,27 +101,11 @@ export async function getThreadMessages(threadId: number): Promise<ThreadHistory
|
||||||
*/
|
*/
|
||||||
export async function appendMessage(
|
export async function appendMessage(
|
||||||
threadId: number,
|
threadId: number,
|
||||||
message: { role: "user" | "assistant" | "system"; content: unknown },
|
message: { role: "user" | "assistant" | "system"; content: unknown }
|
||||||
searchSpaceId?: number
|
|
||||||
): Promise<MessageRecord> {
|
): Promise<MessageRecord> {
|
||||||
const result = await baseApiService.post<MessageRecord>(
|
return baseApiService.post<MessageRecord>(`/api/v1/threads/${threadId}/messages`, undefined, {
|
||||||
`/api/v1/threads/${threadId}/messages`,
|
body: message,
|
||||||
undefined,
|
});
|
||||||
{
|
|
||||||
body: message,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Track message sent event (only for user messages to avoid double-counting)
|
|
||||||
if (message.role === "user" && searchSpaceId) {
|
|
||||||
trackMessageSent({
|
|
||||||
search_space_id: searchSpaceId,
|
|
||||||
thread_id: threadId,
|
|
||||||
role: message.role,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -145,13 +123,8 @@ export async function updateThread(
|
||||||
/**
|
/**
|
||||||
* Delete a thread
|
* Delete a thread
|
||||||
*/
|
*/
|
||||||
export async function deleteThread(threadId: number, searchSpaceId?: number): Promise<void> {
|
export async function deleteThread(threadId: number): Promise<void> {
|
||||||
await baseApiService.delete(`/api/v1/threads/${threadId}`);
|
await baseApiService.delete(`/api/v1/threads/${threadId}`);
|
||||||
|
|
||||||
// Track chat deletion event
|
|
||||||
if (searchSpaceId) {
|
|
||||||
trackChatDeleted({ search_space_id: searchSpaceId, thread_id: threadId });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
@ -245,7 +218,7 @@ export function createThreadListManager(config: ThreadListAdapterConfig) {
|
||||||
|
|
||||||
async deleteThread(threadId: number): Promise<boolean> {
|
async deleteThread(threadId: number): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await deleteThread(threadId, config.searchSpaceId);
|
await deleteThread(threadId);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[ThreadListManager] Failed to delete thread:", error);
|
console.error("[ThreadListManager] Failed to delete thread:", error);
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,6 @@
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"postgres": "^3.4.7",
|
"postgres": "^3.4.7",
|
||||||
"posthog-js": "^1.310.1",
|
|
||||||
"react": "^19.2.3",
|
"react": "^19.2.3",
|
||||||
"react-day-picker": "^9.8.1",
|
"react-day-picker": "^9.8.1",
|
||||||
"react-dom": "^19.2.3",
|
"react-dom": "^19.2.3",
|
||||||
|
|
|
||||||
41
surfsense_web/pnpm-lock.yaml
generated
41
surfsense_web/pnpm-lock.yaml
generated
|
|
@ -188,9 +188,6 @@ importers:
|
||||||
postgres:
|
postgres:
|
||||||
specifier: ^3.4.7
|
specifier: ^3.4.7
|
||||||
version: 3.4.7
|
version: 3.4.7
|
||||||
posthog-js:
|
|
||||||
specifier: ^1.310.1
|
|
||||||
version: 1.310.1
|
|
||||||
react:
|
react:
|
||||||
specifier: ^19.2.3
|
specifier: ^19.2.3
|
||||||
version: 19.2.3
|
version: 19.2.3
|
||||||
|
|
@ -1495,9 +1492,6 @@ packages:
|
||||||
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
|
|
||||||
'@posthog/core@1.9.0':
|
|
||||||
resolution: {integrity: sha512-j7KSWxJTUtNyKynLt/p0hfip/3I46dWU2dk+pt7dKRoz2l5CYueHuHK4EO7Wlgno5yo1HO4sc4s30MXMTICHJw==}
|
|
||||||
|
|
||||||
'@radix-ui/number@1.1.1':
|
'@radix-ui/number@1.1.1':
|
||||||
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
|
resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==}
|
||||||
|
|
||||||
|
|
@ -3279,9 +3273,6 @@ packages:
|
||||||
confbox@0.1.8:
|
confbox@0.1.8:
|
||||||
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
|
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
|
||||||
|
|
||||||
core-js@3.47.0:
|
|
||||||
resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==}
|
|
||||||
|
|
||||||
cose-base@1.0.3:
|
cose-base@1.0.3:
|
||||||
resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
|
resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
|
||||||
|
|
||||||
|
|
@ -3942,9 +3933,6 @@ packages:
|
||||||
picomatch:
|
picomatch:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
fflate@0.4.8:
|
|
||||||
resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==}
|
|
||||||
|
|
||||||
file-entry-cache@8.0.0:
|
file-entry-cache@8.0.0:
|
||||||
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
@ -5227,12 +5215,6 @@ packages:
|
||||||
resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==}
|
resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
posthog-js@1.310.1:
|
|
||||||
resolution: {integrity: sha512-UkR6zzlWNtqHDXHJl2Yk062DOmZyVKTPL5mX4j4V+u3RiYbMHJe47+PpMMUsvK1R2e1r/m9uSlHaJMJRzyUjGg==}
|
|
||||||
|
|
||||||
preact@10.28.1:
|
|
||||||
resolution: {integrity: sha512-u1/ixq/lVQI0CakKNvLDEcW5zfCjUQfZdK9qqWuIJtsezuyG6pk9TWj75GMuI/EzRSZB/VAE43sNWWZfiy8psw==}
|
|
||||||
|
|
||||||
prelude-ls@1.2.1:
|
prelude-ls@1.2.1:
|
||||||
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
@ -6108,9 +6090,6 @@ packages:
|
||||||
web-namespaces@2.0.1:
|
web-namespaces@2.0.1:
|
||||||
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
|
resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
|
||||||
|
|
||||||
web-vitals@4.2.4:
|
|
||||||
resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==}
|
|
||||||
|
|
||||||
webidl-conversions@7.0.0:
|
webidl-conversions@7.0.0:
|
||||||
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
@ -7285,10 +7264,6 @@ snapshots:
|
||||||
'@parcel/watcher-win32-ia32': 2.5.1
|
'@parcel/watcher-win32-ia32': 2.5.1
|
||||||
'@parcel/watcher-win32-x64': 2.5.1
|
'@parcel/watcher-win32-x64': 2.5.1
|
||||||
|
|
||||||
'@posthog/core@1.9.0':
|
|
||||||
dependencies:
|
|
||||||
cross-spawn: 7.0.6
|
|
||||||
|
|
||||||
'@radix-ui/number@1.1.1': {}
|
'@radix-ui/number@1.1.1': {}
|
||||||
|
|
||||||
'@radix-ui/primitive@1.0.0':
|
'@radix-ui/primitive@1.0.0':
|
||||||
|
|
@ -9135,8 +9110,6 @@ snapshots:
|
||||||
|
|
||||||
confbox@0.1.8: {}
|
confbox@0.1.8: {}
|
||||||
|
|
||||||
core-js@3.47.0: {}
|
|
||||||
|
|
||||||
cose-base@1.0.3:
|
cose-base@1.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
layout-base: 1.0.2
|
layout-base: 1.0.2
|
||||||
|
|
@ -9966,8 +9939,6 @@ snapshots:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
picomatch: 4.0.3
|
picomatch: 4.0.3
|
||||||
|
|
||||||
fflate@0.4.8: {}
|
|
||||||
|
|
||||||
file-entry-cache@8.0.0:
|
file-entry-cache@8.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
flat-cache: 4.0.1
|
flat-cache: 4.0.1
|
||||||
|
|
@ -11691,16 +11662,6 @@ snapshots:
|
||||||
|
|
||||||
postgres@3.4.7: {}
|
postgres@3.4.7: {}
|
||||||
|
|
||||||
posthog-js@1.310.1:
|
|
||||||
dependencies:
|
|
||||||
'@posthog/core': 1.9.0
|
|
||||||
core-js: 3.47.0
|
|
||||||
fflate: 0.4.8
|
|
||||||
preact: 10.28.1
|
|
||||||
web-vitals: 4.2.4
|
|
||||||
|
|
||||||
preact@10.28.1: {}
|
|
||||||
|
|
||||||
prelude-ls@1.2.1: {}
|
prelude-ls@1.2.1: {}
|
||||||
|
|
||||||
prismjs@1.27.0: {}
|
prismjs@1.27.0: {}
|
||||||
|
|
@ -12806,8 +12767,6 @@ snapshots:
|
||||||
|
|
||||||
web-namespaces@2.0.1: {}
|
web-namespaces@2.0.1: {}
|
||||||
|
|
||||||
web-vitals@4.2.4: {}
|
|
||||||
|
|
||||||
webidl-conversions@7.0.0: {}
|
webidl-conversions@7.0.0: {}
|
||||||
|
|
||||||
whatwg-encoding@3.1.1:
|
whatwg-encoding@3.1.1:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue