diff --git a/surfsense_web/app/dashboard/[search_space_id]/team/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/team/page.tsx
index 7b8fe49a5..34ad273e1 100644
--- a/surfsense_web/app/dashboard/[search_space_id]/team/page.tsx
+++ b/surfsense_web/app/dashboard/[search_space_id]/team/page.tsx
@@ -13,6 +13,7 @@ import {
Clock,
Copy,
Hash,
+ Link2,
ShieldUser,
Trash2,
UserPlus,
@@ -136,17 +137,6 @@ function getAvatarInitials(member: Membership): string {
return "U";
}
-function getInviteInitials(invite: Invite): string {
- if (invite.name) {
- const parts = invite.name.trim().split(/\s+/);
- if (parts.length >= 2) {
- return (parts[0][0] + parts[1][0]).toUpperCase();
- }
- return invite.name.slice(0, 2).toUpperCase();
- }
- return "IN";
-}
-
const PAGE_SIZE = 10;
export default function TeamManagementPage() {
@@ -247,7 +237,7 @@ export default function TeamManagementPage() {
const nonOwnerMembers = useMemo(() => members.filter((m) => !m.is_owner), [members]);
const [pageIndex, setPageIndex] = useState(0);
- const totalItems = nonOwnerMembers.length + activeInvites.length;
+ const totalItems = nonOwnerMembers.length;
const lastPage = Math.max(0, Math.ceil(totalItems / PAGE_SIZE) - 1);
useEffect(() => {
@@ -263,14 +253,6 @@ export default function TeamManagementPage() {
);
}, [nonOwnerMembers, pageIndex]);
- const paginatedInvites = useMemo(() => {
- const start = pageIndex * PAGE_SIZE;
- const end = start + PAGE_SIZE;
- const inviteStart = Math.max(0, start - nonOwnerMembers.length);
- const inviteEnd = Math.max(0, end - nonOwnerMembers.length);
- return activeInvites.slice(inviteStart, inviteEnd);
- }, [activeInvites, nonOwnerMembers.length, pageIndex]);
-
const displayStart = totalItems > 0 ? pageIndex * PAGE_SIZE + 1 : 0;
const displayEnd = Math.min((pageIndex + 1) * PAGE_SIZE, totalItems);
const canPrev = pageIndex > 0;
@@ -358,7 +340,8 @@ export default function TeamManagementPage() {
{/* Header row: Invite button on left, member count on right */}
-
+
+
{canInvite && (
)}
- {!canInvite &&
}
+ {canInvite && activeInvites.length > 0 && (
+
+ )}
+
{members.length} {members.length === 1 ? "member" : "members"}
@@ -424,16 +413,7 @@ export default function TeamManagementPage() {
index={owners.length + index}
/>
))}
- {paginatedInvites.map((invite, index) => (
-
- ))}
- {members.length === 0 && activeInvites.length === 0 && (
+ {members.length === 0 && (
@@ -650,117 +630,6 @@ function MemberRow({
);
}
-// ============ Invite Row ============
-
-function InviteRow({
- invite,
- canRevoke,
- onRevokeInvite,
- index,
-}: {
- invite: Invite;
- canRevoke: boolean;
- onRevokeInvite: (inviteId: number) => Promise
;
- index: number;
-}) {
- const initials = getInviteInitials(invite);
- const avatarColor = getAvatarColor(invite.invite_code);
- const displayName = invite.name || "Unnamed Invite";
-
- return (
-
-
-
-
- {initials}
-
-
-
{displayName}
- {invite.role?.name && (
-
- Will join as {invite.role.name}
-
- )}
-
-
-
-
-
- Never
-
-
-
- {canRevoke ? (
-
-
-
-
- e.preventDefault()}>
- {
- const link = `${window.location.origin}/invite/${invite.invite_code}`;
- navigator.clipboard.writeText(link);
- toast.success("Invite link copied");
- }}
- >
-
- Copy invite link
-
-
-
-
- e.preventDefault()}
- >
-
- Revoke invite
-
-
-
-
- Revoke invite?
-
- This will permanently delete this invite link. Anyone with this link
- will no longer be able to join.
-
-
-
- Cancel
- onRevokeInvite(invite.id)}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
- >
- Revoke
-
-
-
-
-
-
- ) : (
- Invited
- )}
-
-
- );
-}
-
// ============ Create Invite Dialog ============
function CreateInviteDialog({
@@ -843,8 +712,8 @@ function CreateInviteDialog({
Invite members
-
- {createdInvite ? (
+ e.preventDefault()}>
+ {createdInvite ? (
<>
@@ -989,3 +858,119 @@ function CreateInviteDialog({
);
}
+
+// ============ All Invites Dialog ============
+
+function AllInvitesDialog({
+ invites,
+ onRevokeInvite,
+}: {
+ invites: Invite[];
+ onRevokeInvite: (inviteId: number) => Promise;
+}) {
+ const [copiedId, setCopiedId] = useState(null);
+
+ const copyLink = (invite: Invite) => {
+ const link = `${window.location.origin}/invite/${invite.invite_code}`;
+ navigator.clipboard.writeText(link);
+ setCopiedId(invite.id);
+ toast.success("Invite link copied");
+ setTimeout(() => setCopiedId(null), 2000);
+ };
+
+ return (
+
+ );
+}
diff --git a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx
index aee98a290..4e95c381f 100644
--- a/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx
+++ b/surfsense_web/components/layout/ui/sidebar/Sidebar.tsx
@@ -93,7 +93,7 @@ export function Sidebar({
return (
-
+
-
+
{t("manage_members")}
-
+
{t("search_space_settings")}
diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx
index 3ba7bb2a0..7f3a97f93 100644
--- a/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx
+++ b/surfsense_web/components/layout/ui/sidebar/SidebarUserProfile.tsx
@@ -177,7 +177,7 @@ export function SidebarUserProfile({
{displayName}
-
+
@@ -191,14 +191,14 @@ export function SidebarUserProfile({
-
+
{t("user_settings")}
{setTheme && (
-
+
{t("theme")}
@@ -216,7 +216,7 @@ export function SidebarUserProfile({
isSelected && "text-primary"
)}
>
-
+
{t(themeOption.value)}
{isSelected && }
@@ -229,7 +229,7 @@ export function SidebarUserProfile({
-
+
{t("language")}
@@ -262,7 +262,7 @@ export function SidebarUserProfile({
{isLoggingOut ? (
) : (
-
+
)}
{isLoggingOut ? t("loggingOut") : t("logout")}
@@ -299,7 +299,7 @@ export function SidebarUserProfile({
-
+
@@ -313,14 +313,14 @@ export function SidebarUserProfile({
-
+
{t("user_settings")}
{setTheme && (
-
+
{t("theme")}
@@ -338,7 +338,7 @@ export function SidebarUserProfile({
isSelected && "text-primary"
)}
>
-
+
{t(themeOption.value)}
{isSelected && }
@@ -351,7 +351,7 @@ export function SidebarUserProfile({
-
+
{t("language")}
@@ -384,7 +384,7 @@ export function SidebarUserProfile({
{isLoggingOut ? (
) : (
-
+
)}
{isLoggingOut ? t("loggingOut") : t("logout")}
diff --git a/surfsense_web/components/new-chat/model-selector.tsx b/surfsense_web/components/new-chat/model-selector.tsx
index 98911fe38..d894fe252 100644
--- a/surfsense_web/components/new-chat/model-selector.tsx
+++ b/surfsense_web/components/new-chat/model-selector.tsx
@@ -226,7 +226,7 @@ export function ModelSelector({
size="sm"
role="combobox"
aria-expanded={open}
- className={cn("h-8 gap-2 px-3 text-sm border-border/60", className)}
+ className={cn("h-8 gap-2 px-3 text-sm border-border/60 select-none", className)}
>
{isLoading ? (
<>
@@ -280,7 +280,7 @@ export function ModelSelector({