mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-28 18:36:23 +02:00
Merge remote-tracking branch 'upstream/dev' into sur-70-feature-streamline-onboarding-auto-create-default-workspace
This commit is contained in:
commit
a5d47cae31
133 changed files with 5763 additions and 2603 deletions
|
|
@ -5,7 +5,7 @@ import { Navbar } from "@/components/homepage/navbar";
|
|||
|
||||
export default function HomePageLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<main className="min-h-screen bg-gradient-to-b from-gray-50 to-gray-100 text-gray-900 dark:from-black dark:to-gray-900 dark:text-white overflow-x-hidden">
|
||||
<main className="min-h-screen bg-linear-to-b from-gray-50 to-gray-100 text-gray-900 dark:from-black dark:to-gray-900 dark:text-white overflow-x-hidden">
|
||||
<Navbar />
|
||||
{children}
|
||||
<FooterNew />
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import {
|
|||
} from "lucide-react";
|
||||
import { motion } from "motion/react";
|
||||
import { useParams, useRouter } from "next/navigation";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
createInviteMutationAtom,
|
||||
|
|
@ -116,6 +116,7 @@ import type {
|
|||
} from "@/contracts/types/roles.types";
|
||||
import { invitesApiService } from "@/lib/apis/invites-api.service";
|
||||
import { rolesApiService } from "@/lib/apis/roles-api.service";
|
||||
import { trackSearchSpaceInviteSent, trackSearchSpaceUsersViewed } from "@/lib/posthog/events";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
|
|
@ -297,6 +298,14 @@ export default function TeamManagementPage() {
|
|||
toast.success("Team data refreshed");
|
||||
}, [fetchMembers, fetchRoles, fetchInvites]);
|
||||
|
||||
// Track users per search space when team page is viewed
|
||||
useEffect(() => {
|
||||
if (members.length > 0 && !membersLoading) {
|
||||
const ownerCount = members.filter((m) => m.is_owner).length;
|
||||
trackSearchSpaceUsersViewed(searchSpaceId, members.length, ownerCount);
|
||||
}
|
||||
}, [members, membersLoading, searchSpaceId]);
|
||||
|
||||
if (accessLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[60vh]">
|
||||
|
|
@ -1088,10 +1097,12 @@ function InvitesTab({
|
|||
function CreateInviteDialog({
|
||||
roles,
|
||||
onCreateInvite,
|
||||
searchSpaceId,
|
||||
className,
|
||||
}: {
|
||||
roles: Role[];
|
||||
onCreateInvite: (data: CreateInviteRequest["data"]) => Promise<Invite>;
|
||||
searchSpaceId: number;
|
||||
className?: string;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
|
@ -1114,6 +1125,17 @@ function CreateInviteDialog({
|
|||
|
||||
const invite = await onCreateInvite(data);
|
||||
setCreatedInvite(invite);
|
||||
|
||||
// Track invite sent event
|
||||
const roleName =
|
||||
roleId && roleId !== "default"
|
||||
? roles.find((r) => r.id.toString() === roleId)?.name
|
||||
: undefined;
|
||||
trackSearchSpaceInviteSent(searchSpaceId, {
|
||||
roleName,
|
||||
hasExpiry: !!expiresAt,
|
||||
hasMaxUses: !!maxUses,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to create invite:", error);
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ const DashboardPage = () => {
|
|||
email:
|
||||
user?.email ||
|
||||
(isLoadingUser ? "Loading..." : userError ? "Error loading user" : "Unknown User"),
|
||||
avatar: "/icon-128.png", // Default avatar
|
||||
avatar: "/icon-128.svg", // Default avatar
|
||||
};
|
||||
|
||||
// Show loading while loading or auto-redirecting (single search space)
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
|
@ -33,6 +33,11 @@ import {
|
|||
import type { AcceptInviteResponse } from "@/contracts/types/invites.types";
|
||||
import { invitesApiService } from "@/lib/apis/invites-api.service";
|
||||
import { getBearerToken } from "@/lib/auth-utils";
|
||||
import {
|
||||
trackSearchSpaceInviteAccepted,
|
||||
trackSearchSpaceInviteDeclined,
|
||||
trackSearchSpaceUserAdded,
|
||||
} from "@/lib/posthog/events";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
|
||||
export default function InviteAcceptPage() {
|
||||
|
|
@ -91,6 +96,18 @@ export default function InviteAcceptPage() {
|
|||
if (result) {
|
||||
setAccepted(true);
|
||||
setAcceptedData(result);
|
||||
|
||||
// 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.role_name
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err.message || "Failed to accept invite");
|
||||
|
|
@ -99,6 +116,12 @@ export default function InviteAcceptPage() {
|
|||
}
|
||||
};
|
||||
|
||||
const handleDecline = () => {
|
||||
// Track invite declined event
|
||||
trackSearchSpaceInviteDeclined(inviteInfo?.search_space_name);
|
||||
router.push("/dashboard");
|
||||
};
|
||||
|
||||
const handleLoginRedirect = () => {
|
||||
// Store the invite code to redirect back after login
|
||||
localStorage.setItem("pending_invite_code", inviteCode);
|
||||
|
|
@ -324,11 +347,7 @@ export default function InviteAcceptPage() {
|
|||
)}
|
||||
</CardContent>
|
||||
<CardFooter className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex-1"
|
||||
onClick={() => router.push("/dashboard")}
|
||||
>
|
||||
<Button variant="outline" className="flex-1" onClick={handleDecline}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button className="flex-1 gap-2" onClick={handleAccept} disabled={accepting}>
|
||||
|
|
@ -360,7 +379,7 @@ export default function InviteAcceptPage() {
|
|||
href="/"
|
||||
className="inline-flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<Image src="/icon-128.png" alt="SurfSense" width={24} height={24} className="rounded" />
|
||||
<Image src="/icon-128.svg" alt="SurfSense" width={24} height={24} className="rounded" />
|
||||
<span className="text-sm font-medium">SurfSense</span>
|
||||
</Link>
|
||||
</motion.div>
|
||||
|
|
|
|||
|
|
@ -1,60 +1,179 @@
|
|||
import type { MetadataRoute } from "next";
|
||||
|
||||
// Returns a date rounded to the current hour (updates only once per hour)
|
||||
function getHourlyDate(): Date {
|
||||
const now = new Date();
|
||||
now.setMinutes(0, 0, 0);
|
||||
return now;
|
||||
}
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const lastModified = getHourlyDate();
|
||||
|
||||
return [
|
||||
{
|
||||
url: "https://www.surfsense.com/",
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "yearly",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 1,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/contact",
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "yearly",
|
||||
priority: 1,
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/pricing",
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "yearly",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/privacy",
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/terms",
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "monthly",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.9,
|
||||
},
|
||||
// Documentation pages
|
||||
{
|
||||
url: "https://www.surfsense.com/docs",
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
priority: 0.9,
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 1,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/installation",
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/docker-installation",
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.9,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/manual-installation",
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.9,
|
||||
},
|
||||
// Connector documentation
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/airtable",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/bookstack",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/circleback",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/clickup",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/confluence",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/discord",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/elasticsearch",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/github",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/gmail",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/google-calendar",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/google-drive",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/jira",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/linear",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/luma",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/microsoft-teams",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/notion",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/slack",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
{
|
||||
url: "https://www.surfsense.com/docs/connectors/web-crawler",
|
||||
lastModified,
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue