Merge remote-tracking branch 'upstream/dev' into fixes-playground-ui

# Conflicts:
#	surfsense_web/lib/apis/base-api.service.ts
This commit is contained in:
CREDO23 2026-07-24 08:29:39 +02:00
commit 06c7e27c72
87 changed files with 2882 additions and 1673 deletions

View file

@ -13,7 +13,7 @@ import { Spinner } from "@/components/ui/spinner";
import { getAuthErrorDetails, isNetworkError } from "@/lib/auth-errors";
import { getPostLoginRedirectPath } from "@/lib/auth-utils";
import { ValidationError } from "@/lib/error";
import { trackLoginAttempt, trackLoginFailure, trackLoginSuccess } from "@/lib/posthog/events";
import { trackLoginAttempt, trackLoginFailure } from "@/lib/posthog/events";
export function LocalLoginForm() {
const t = useTranslations("auth");
@ -45,8 +45,8 @@ export function LocalLoginForm() {
grant_type: "password",
});
// Track successful login
trackLoginSuccess("local");
// auth_login_success is now emitted server-side
// (UserManager.on_after_login) — authoritative vs. optimistic.
// Small delay to show success message
setTimeout(() => {

View file

@ -15,11 +15,7 @@ import { Spinner } from "@/components/ui/spinner";
import { useSession } from "@/hooks/use-session";
import { getAuthErrorDetails, isNetworkError, shouldRetry } from "@/lib/auth-errors";
import { AppError, ValidationError } from "@/lib/error";
import {
trackRegistrationAttempt,
trackRegistrationFailure,
trackRegistrationSuccess,
} from "@/lib/posthog/events";
import { trackRegistrationAttempt, trackRegistrationFailure } from "@/lib/posthog/events";
import { AmbientBackground } from "../login/AmbientBackground";
export default function RegisterPage() {
@ -81,8 +77,8 @@ export default function RegisterPage() {
is_verified: false,
});
// Track successful registration
trackRegistrationSuccess();
// auth_registration_success is now emitted server-side
// (UserManager.on_after_register) — authoritative vs. optimistic.
// Success toast
toast.success(t("register_success"), {

View file

@ -8,7 +8,6 @@ import { useEffect } from "react";
import { pendingUserImageDataUrlsAtom } from "@/atoms/chat/pending-user-images.atom";
import { llmSetupStatusAtomFamily } from "@/atoms/model-connections/model-connections-query.atoms";
import { activeWorkspaceIdAtom } from "@/atoms/workspaces/workspace-query.atoms";
import { ConnectorIndicator } from "@/components/assistant-ui/connector-popup";
import { DocumentUploadDialogProvider } from "@/components/assistant-ui/document-upload-popup";
import { LayoutDataProvider } from "@/components/layout";
import { OnboardingTour } from "@/components/onboarding-tour";
@ -169,7 +168,6 @@ export function DashboardClientLayout({
initialPlaygroundSidebarCollapsed={initialPlaygroundSidebarCollapsed}
>
{children}
<ConnectorIndicator showTrigger={false} />
</LayoutDataProvider>
</DocumentUploadDialogProvider>
);

View file

@ -19,7 +19,9 @@ export async function GET(
const response = new NextResponse(null, {
status: 302,
headers: {
Location: `/dashboard/${workspace_id}/new-chat`,
// Land on the connectors panel so `useConnectorDialog` (mounted there)
// consumes the result cookie and continues the indexing/edit flow.
Location: `/dashboard/${workspace_id}/connectors`,
},
});
response.cookies.set(OAUTH_RESULT_COOKIE, result, {

View file

@ -0,0 +1,5 @@
import { ConnectorsSection } from "@/components/assistant-ui/connector-popup/connectors-panel";
export default function ConnectorsPage() {
return <ConnectorsSection />;
}

View file

@ -96,7 +96,6 @@ import type { Role } from "@/contracts/types/roles.types";
import { invitesApiService } from "@/lib/apis/invites-api.service";
import { rolesApiService } from "@/lib/apis/roles-api.service";
import { formatRelativeDate } from "@/lib/format-date";
import { trackWorkspaceInviteSent, trackWorkspaceUsersViewed } from "@/lib/posthog/events";
import { cacheKeys } from "@/lib/query-client/cache-keys";
import { cn } from "@/lib/utils";
@ -226,12 +225,7 @@ export function TeamContent({ workspaceId }: TeamContentProps) {
const canPrev = pageIndex > 0;
const canNext = displayEnd < totalItems;
useEffect(() => {
if (members.length > 0 && !membersLoading) {
const ownerCount = members.filter((m) => m.is_owner).length;
trackWorkspaceUsersViewed(workspaceId, members.length, ownerCount);
}
}, [members, membersLoading, workspaceId]);
// workspace_users_viewed removed — redundant with $pageview.
if (accessLoading || membersLoading) {
return (
@ -342,11 +336,7 @@ export function TeamContent({ workspaceId }: TeamContentProps) {
Invite members
</Button>
) : (
<CreateInviteDialog
roles={roles}
onCreateInvite={handleCreateInvite}
workspaceId={workspaceId}
/>
<CreateInviteDialog roles={roles} onCreateInvite={handleCreateInvite} />
)}
{invitesLoading ? (
<Button
@ -617,11 +607,9 @@ function MemberRow({
function CreateInviteDialog({
roles,
onCreateInvite,
workspaceId,
}: {
roles: Role[];
onCreateInvite: (data: CreateInviteRequest["data"]) => Promise<Invite>;
workspaceId: number;
}) {
const [open, setOpen] = useState(false);
const [creating, setCreating] = useState(false);
@ -653,12 +641,7 @@ function CreateInviteDialog({
const invite = await onCreateInvite(data);
setCreatedInvite(invite);
const roleName = roleId ? roles.find((r) => r.id.toString() === roleId)?.name : undefined;
trackWorkspaceInviteSent(workspaceId, {
roleName,
hasExpiry: !!expiresAt,
hasMaxUses: !!maxUses,
});
// workspace_invite_sent is now emitted server-side (rbac_routes.create_invite).
} catch (error) {
console.error("Failed to create invite:", error);
toast.error("Failed to create invite. Please try again.");

View file

@ -43,21 +43,15 @@ export default function DashboardError({
<Button type="button" onClick={reset}>
Try again
</Button>
<Link
href="/dashboard"
className="rounded-md border border-input bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-colors"
>
Go to dashboard home
</Link>
<a
href={issueUrl}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 rounded-md border border-input bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground transition-colors"
>
<ExternalLink className="h-3.5 w-3.5" />
Report Issue
</a>
<Button asChild variant="ghost">
<Link href="/dashboard">Back to dashboard</Link>
</Button>
<Button asChild variant="ghost">
<a href={issueUrl} target="_blank" rel="noopener noreferrer">
Report Issue
<ExternalLink className="h-3.5 w-3.5" />
</a>
</Button>
</div>
</div>
);

View file

@ -16,7 +16,7 @@ import { motion } from "motion/react";
import Image from "next/image";
import Link from "next/link";
import { useParams, useRouter } from "next/navigation";
import { use, useCallback, useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { toast } from "sonner";
import { acceptInviteMutationAtom } from "@/atoms/invites/invites-mutation.atoms";
import { Button } from "@/components/ui/button";
@ -33,11 +33,7 @@ import type { AcceptInviteResponse } from "@/contracts/types/invites.types";
import { useSession } from "@/hooks/use-session";
import { invitesApiService } from "@/lib/apis/invites-api.service";
import { setRedirectPath } from "@/lib/auth-utils";
import {
trackWorkspaceInviteAccepted,
trackWorkspaceInviteDeclined,
trackWorkspaceUserAdded,
} from "@/lib/posthog/events";
import { trackWorkspaceInviteDeclined } from "@/lib/posthog/events";
import { cacheKeys } from "@/lib/query-client/cache-keys";
export default function InviteAcceptPage() {
@ -96,9 +92,9 @@ export default function InviteAcceptPage() {
setAccepted(true);
setAcceptedData(result);
// Track invite accepted and user added events
trackWorkspaceInviteAccepted(result.workspace_id, result.workspace_name, result.role_name);
trackWorkspaceUserAdded(result.workspace_id, result.workspace_name, result.role_name);
// workspace_invite_accepted + workspace_user_added are now emitted
// server-side (rbac_routes.accept_invite) — the server redirect is
// the authoritative join point.
}
} catch (err: any) {
setError(err.message || "Failed to accept invite");