organify-ui 0.3.39 → 0.3.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1295,14 +1295,13 @@ function MessageInput({
1295
1295
  const [caretPosition, setCaretPosition] = React6.useState(null);
1296
1296
  const detectMention = (text, cursorPos) => {
1297
1297
  const beforeCursor = text.slice(0, cursorPos);
1298
- const match = beforeCursor.match(/@(\w*)$/);
1298
+ const match = beforeCursor.match(/@([^@\n\r]*)$/);
1299
1299
  if (match) {
1300
1300
  setShowMentions(true);
1301
1301
  setMentionQuery(match[1]);
1302
1302
  setMentionIndex(0);
1303
1303
  onMentionSearch?.(match[1], null);
1304
1304
  if (inputRef.current) {
1305
- inputRef.current.getBoundingClientRect();
1306
1305
  setCaretPosition({
1307
1306
  top: -200,
1308
1307
  // Above input
@@ -1354,7 +1353,7 @@ function MessageInput({
1354
1353
  const cursorPos = inputRef.current?.selectionStart || 0;
1355
1354
  const beforeCursor = value.slice(0, cursorPos);
1356
1355
  const afterCursor = value.slice(cursorPos);
1357
- const match = beforeCursor.match(/@(\w*)$/);
1356
+ const match = beforeCursor.match(/@([^@\n\r]*)$/);
1358
1357
  if (match) {
1359
1358
  const mentionStart = beforeCursor.length - match[0].length;
1360
1359
  const newValue = beforeCursor.slice(0, mentionStart) + `@${option.display} ` + afterCursor;
@@ -2547,15 +2546,20 @@ function RoomManagementPanel({
2547
2546
  permissions,
2548
2547
  currentUserId,
2549
2548
  loading,
2549
+ workspaceMembers,
2550
2550
  onClose,
2551
2551
  onUpdateRoom,
2552
2552
  onArchiveRoom,
2553
2553
  onLeaveRoom,
2554
2554
  onRemoveMember,
2555
2555
  onUpdateMemberRole,
2556
+ onAddMember,
2556
2557
  className
2557
2558
  }) {
2558
2559
  const [view, setView] = React6.useState("info");
2560
+ const [addMemberSearch, setAddMemberSearch] = React6.useState("");
2561
+ const [addMemberOpen, setAddMemberOpen] = React6.useState(false);
2562
+ const [addingMember, setAddingMember] = React6.useState(false);
2559
2563
  const [editName, setEditName] = React6.useState(room.name || "");
2560
2564
  const [editDesc, setEditDesc] = React6.useState(room.description || "");
2561
2565
  const [editVisibility, setEditVisibility] = React6.useState(room.visibility);
@@ -2706,65 +2710,129 @@ function RoomManagementPanel({
2706
2710
  ] });
2707
2711
  const renderMembers = () => /* @__PURE__ */ jsxs(Fragment, { children: [
2708
2712
  renderHeader("Membros", true),
2709
- /* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx("div", { className: "p-3 space-y-0.5", children: loading ? Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-2", children: [
2710
- /* @__PURE__ */ jsx(Skeleton, { variant: "circular", className: "h-8 w-8" }),
2711
- /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1", children: [
2712
- /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" }),
2713
- /* @__PURE__ */ jsx(Skeleton, { className: "h-2 w-16" })
2714
- ] })
2715
- ] }, i)) : members.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-theme-muted text-center py-8", children: "Sem membros" }) : members.map((member) => /* @__PURE__ */ jsxs(
2716
- "div",
2717
- {
2718
- className: "flex items-center gap-3 p-2 rounded-xl hover:bg-theme-subtle group transition-colors",
2719
- children: [
2720
- /* @__PURE__ */ jsx(Avatar, { shape: "circle", size: "sm", className: "h-8 w-8", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/20 text-primary-light text-xs", children: getInitials4(member.displayName || member.userId.substring(0, 8)) }) }),
2721
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2722
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2723
- /* @__PURE__ */ jsx("span", { className: "text-[13px] font-medium text-theme truncate", children: member.displayName || member.userId.substring(0, 8) }),
2724
- String(member.userId) === String(currentUserId) && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-theme-muted", children: "(voc\xEA)" })
2713
+ /* @__PURE__ */ jsxs(ScrollArea, { className: "flex-1", children: [
2714
+ /* @__PURE__ */ jsx("div", { className: "p-3 space-y-0.5", children: loading ? Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-2", children: [
2715
+ /* @__PURE__ */ jsx(Skeleton, { variant: "circular", className: "h-8 w-8" }),
2716
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1", children: [
2717
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" }),
2718
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-2 w-16" })
2719
+ ] })
2720
+ ] }, i)) : members.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-theme-muted text-center py-8", children: "Sem membros" }) : members.map((member) => /* @__PURE__ */ jsxs(
2721
+ "div",
2722
+ {
2723
+ className: "flex items-center gap-3 p-2 rounded-xl hover:bg-theme-subtle group transition-colors",
2724
+ children: [
2725
+ /* @__PURE__ */ jsx(Avatar, { shape: "circle", size: "sm", className: "h-8 w-8", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/20 text-primary-light text-xs", children: getInitials4(
2726
+ member.displayName || workspaceMembers?.find((w) => w.id === member.userId)?.displayName || member.userId.substring(0, 8)
2727
+ ) }) }),
2728
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
2729
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2730
+ /* @__PURE__ */ jsx("span", { className: "text-[13px] font-medium text-theme truncate", children: member.displayName || workspaceMembers?.find((w) => w.id === member.userId)?.displayName || member.userId.substring(0, 8) }),
2731
+ String(member.userId) === String(currentUserId) && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-theme-muted", children: "(voc\xEA)" })
2732
+ ] }),
2733
+ /* @__PURE__ */ jsx(
2734
+ Badge,
2735
+ {
2736
+ variant: "default",
2737
+ className: cn(
2738
+ "text-[9px] px-1 py-0 h-3.5 border mt-0.5",
2739
+ roleBadgeColors[member.role]
2740
+ ),
2741
+ children: roleLabels[member.role]
2742
+ }
2743
+ )
2725
2744
  ] }),
2726
- /* @__PURE__ */ jsx(
2727
- Badge,
2728
- {
2729
- variant: "default",
2730
- className: cn(
2731
- "text-[9px] px-1 py-0 h-3.5 border mt-0.5",
2732
- roleBadgeColors[member.role]
2733
- ),
2734
- children: roleLabels[member.role]
2735
- }
2736
- )
2737
- ] }),
2738
- permissions.canManageMembers && String(member.userId) !== String(currentUserId) && member.role !== "OWNER" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
2739
- /* @__PURE__ */ jsxs(
2740
- Select,
2741
- {
2742
- value: member.role,
2743
- onValueChange: (val) => onUpdateMemberRole(room.id, member.userId, val),
2744
- children: [
2745
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-6 w-20 text-[10px] bg-theme-subtle border-theme-subtle-10", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
2746
- /* @__PURE__ */ jsxs(SelectContent, { children: [
2747
- /* @__PURE__ */ jsx(SelectItem, { value: "ADMIN", children: "Admin" }),
2748
- /* @__PURE__ */ jsx(SelectItem, { value: "MEMBER", children: "Membro" })
2749
- ] })
2750
- ]
2751
- }
2752
- ),
2753
- /* @__PURE__ */ jsx(
2754
- Button,
2755
- {
2756
- variant: "ghost",
2757
- size: "sm",
2758
- onClick: () => onRemoveMember(room.id, member.userId),
2759
- className: "h-6 w-6 p-0 text-theme-muted hover:text-rose-400 hover:bg-rose-500/10",
2760
- children: /* @__PURE__ */ jsx(OrgClose, { className: "w-3 h-3" })
2761
- }
2762
- )
2763
- ] })
2764
- ]
2765
- },
2766
- member.id
2767
- )) }) })
2745
+ permissions.canManageMembers && String(member.userId) !== String(currentUserId) && member.role !== "OWNER" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
2746
+ /* @__PURE__ */ jsxs(
2747
+ Select,
2748
+ {
2749
+ value: member.role,
2750
+ onValueChange: (val) => onUpdateMemberRole(room.id, member.userId, val),
2751
+ children: [
2752
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "h-6 w-20 text-[10px] bg-theme-subtle border-theme-subtle-10", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
2753
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
2754
+ /* @__PURE__ */ jsx(SelectItem, { value: "ADMIN", children: "Admin" }),
2755
+ /* @__PURE__ */ jsx(SelectItem, { value: "MEMBER", children: "Membro" })
2756
+ ] })
2757
+ ]
2758
+ }
2759
+ ),
2760
+ /* @__PURE__ */ jsx(
2761
+ Button,
2762
+ {
2763
+ variant: "ghost",
2764
+ size: "sm",
2765
+ onClick: () => onRemoveMember(room.id, member.userId),
2766
+ className: "h-6 w-6 p-0 text-theme-muted hover:text-rose-400 hover:bg-rose-500/10",
2767
+ children: /* @__PURE__ */ jsx(OrgClose, { className: "w-3 h-3" })
2768
+ }
2769
+ )
2770
+ ] })
2771
+ ]
2772
+ },
2773
+ member.id
2774
+ )) }),
2775
+ permissions.canManageMembers && onAddMember && room.type !== "DIRECT" && /* @__PURE__ */ jsx("div", { className: "px-3 pb-3", children: !addMemberOpen ? /* @__PURE__ */ jsxs(
2776
+ "button",
2777
+ {
2778
+ onClick: () => setAddMemberOpen(true),
2779
+ className: "flex items-center gap-2 w-full px-3 py-2 rounded-xl text-xs text-primary hover:bg-primary/10 border border-dashed border-primary/30 transition-colors",
2780
+ children: [
2781
+ /* @__PURE__ */ jsx("span", { className: "text-base leading-none", children: "+" }),
2782
+ "Adicionar membro"
2783
+ ]
2784
+ }
2785
+ ) : /* @__PURE__ */ jsxs("div", { className: "space-y-2 animate-in fade-in-0 duration-[300ms]", children: [
2786
+ /* @__PURE__ */ jsx(
2787
+ Input,
2788
+ {
2789
+ variant: "flat",
2790
+ placeholder: "Pesquisar membro...",
2791
+ value: addMemberSearch,
2792
+ onChange: (e) => setAddMemberSearch(e.target.value),
2793
+ autoFocus: true,
2794
+ className: "h-8 text-xs"
2795
+ }
2796
+ ),
2797
+ /* @__PURE__ */ jsxs("div", { className: "max-h-40 overflow-y-auto space-y-0.5", children: [
2798
+ (workspaceMembers ?? []).filter(
2799
+ (w) => !members.some((m) => m.userId === w.id) && (addMemberSearch.trim() === "" || w.displayName.toLowerCase().includes(addMemberSearch.toLowerCase()) || (w.email ?? "").toLowerCase().includes(addMemberSearch.toLowerCase()))
2800
+ ).slice(0, 8).map((w) => /* @__PURE__ */ jsxs(
2801
+ "button",
2802
+ {
2803
+ disabled: addingMember,
2804
+ onClick: async () => {
2805
+ setAddingMember(true);
2806
+ await onAddMember(room.id, w.id);
2807
+ setAddingMember(false);
2808
+ setAddMemberOpen(false);
2809
+ setAddMemberSearch("");
2810
+ },
2811
+ className: "flex items-center gap-2 w-full px-2 py-1.5 rounded-lg text-xs text-theme hover:bg-theme-subtle transition-colors text-left",
2812
+ children: [
2813
+ /* @__PURE__ */ jsx(Avatar, { shape: "circle", size: "sm", className: "h-6 w-6", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/20 text-primary-light text-[9px]", children: getInitials4(w.displayName) }) }),
2814
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: w.displayName })
2815
+ ]
2816
+ },
2817
+ w.id
2818
+ )),
2819
+ (workspaceMembers ?? []).filter(
2820
+ (w) => !members.some((m) => m.userId === w.id)
2821
+ ).length === 0 && /* @__PURE__ */ jsx("p", { className: "text-xs text-theme-muted text-center py-2", children: "Todos os membros j\xE1 adicionados" })
2822
+ ] }),
2823
+ /* @__PURE__ */ jsx(
2824
+ "button",
2825
+ {
2826
+ onClick: () => {
2827
+ setAddMemberOpen(false);
2828
+ setAddMemberSearch("");
2829
+ },
2830
+ className: "text-[10px] text-theme-muted hover:text-theme transition-colors w-full text-right pr-1",
2831
+ children: "Cancelar"
2832
+ }
2833
+ )
2834
+ ] }) })
2835
+ ] })
2768
2836
  ] });
2769
2837
  const renderEdit = () => /* @__PURE__ */ jsxs(Fragment, { children: [
2770
2838
  renderHeader("Editar canal", true),
@@ -2908,6 +2976,7 @@ function useChat(options = {}) {
2908
2976
  const [myRoomRole, setMyRoomRole] = useState(null);
2909
2977
  const [typingUsers, setTypingUsers] = useState([]);
2910
2978
  const [error, setError] = useState(null);
2979
+ const joinedRoomsRef = React6__default.useRef(/* @__PURE__ */ new Set());
2911
2980
  const isWorkspaceAdmin = workspaceRole === "OWNER" || workspaceRole === "ADMIN";
2912
2981
  const isDemoMode = !!(initialRooms && initialRooms.length > 0);
2913
2982
  const permissions = getRoomPermissions(myRoomRole, isWorkspaceAdmin);
@@ -3095,14 +3164,43 @@ function useChat(options = {}) {
3095
3164
  { roomId }
3096
3165
  );
3097
3166
  const members = data?.room?.members ?? [];
3098
- setRoomMembers(members);
3099
- const myMembership = members.find((m) => String(m.userId) === String(userId));
3167
+ const enriched = members.map((m) => {
3168
+ if (!m.displayName) {
3169
+ const cached = userCache.get(m.userId);
3170
+ return { ...m, displayName: cached?.displayName || void 0 };
3171
+ }
3172
+ return m;
3173
+ });
3174
+ setRoomMembers(enriched);
3175
+ const myMembership = enriched.find((m) => String(m.userId) === String(userId));
3100
3176
  setMyRoomRole(myMembership?.role ?? null);
3101
3177
  } catch (err) {
3102
3178
  console.error("[organify-chat] fetchRoomMembers:", err);
3103
3179
  }
3104
3180
  },
3105
- [gql, userId, isDemoMode]
3181
+ [gql, userId, isDemoMode, userCache]
3182
+ );
3183
+ const joinRoomIfNeeded = useCallback(
3184
+ async (roomId, roomType) => {
3185
+ if (isDemoMode || roomType === "DIRECT") return;
3186
+ if (joinedRoomsRef.current.has(roomId)) return;
3187
+ joinedRoomsRef.current.add(roomId);
3188
+ try {
3189
+ await gql(
3190
+ `mutation($roomId: ID!) { joinRoom(roomId: $roomId) { id userId role joinedAt } }`,
3191
+ { roomId }
3192
+ );
3193
+ fetchRoomMembers(roomId);
3194
+ setRooms(
3195
+ (prev) => prev.map(
3196
+ (r) => r.id === roomId ? { ...r, memberCount: Math.max(r.memberCount, (r.memberCount || 0) + 1) } : r
3197
+ )
3198
+ );
3199
+ } catch {
3200
+ joinedRoomsRef.current.delete(roomId);
3201
+ }
3202
+ },
3203
+ [isDemoMode, gql, fetchRoomMembers]
3106
3204
  );
3107
3205
  const selectRoom = useCallback(
3108
3206
  (roomId) => {
@@ -3126,8 +3224,10 @@ function useChat(options = {}) {
3126
3224
  setMessageCursor(null);
3127
3225
  fetchMessages(roomId);
3128
3226
  fetchRoomMembers(roomId);
3227
+ const room = rooms.find((r) => r.id === roomId);
3228
+ if (room) joinRoomIfNeeded(roomId, room.type);
3129
3229
  },
3130
- [activeRoomId, workspaceId, fetchMessages, fetchRoomMembers]
3230
+ [activeRoomId, workspaceId, fetchMessages, fetchRoomMembers, rooms, joinRoomIfNeeded]
3131
3231
  );
3132
3232
  const sendMessage = useCallback(
3133
3233
  async (content, replyToId) => {
@@ -3168,9 +3268,7 @@ function useChat(options = {}) {
3168
3268
  { input }
3169
3269
  );
3170
3270
  if (data?.sendMessage) {
3171
- setMessages(
3172
- (prev) => prev.map((m) => m.id === tempId ? { ...m, _status: "sent" } : m)
3173
- );
3271
+ setMessages((prev) => prev.filter((m) => m.id !== tempId));
3174
3272
  } else {
3175
3273
  setMessages(
3176
3274
  (prev) => prev.map((m) => m.id === tempId ? { ...m, _status: "error" } : m)
@@ -3645,16 +3743,6 @@ function useChat(options = {}) {
3645
3743
  const newMsg = msg.payload.data.messageCreated;
3646
3744
  setMessages((prev) => {
3647
3745
  if (prev.some((m) => m.id === newMsg.id)) return prev;
3648
- if (String(newMsg.authorId) === String(userId)) {
3649
- const ownIdx = prev.findIndex(
3650
- (m) => (m._status === "pending" || m._status === "sent") && m.content === newMsg.content && String(m.authorId) === String(newMsg.authorId)
3651
- );
3652
- if (ownIdx !== -1) {
3653
- const next = [...prev];
3654
- next[ownIdx] = { ...newMsg, _status: "sent" };
3655
- return next;
3656
- }
3657
- }
3658
3746
  return [...prev, newMsg];
3659
3747
  });
3660
3748
  }
@@ -3777,7 +3865,9 @@ function useChat(options = {}) {
3777
3865
  // Refresh
3778
3866
  fetchRooms,
3779
3867
  fetchRoomMembers,
3780
- userCache
3868
+ userCache,
3869
+ // Join
3870
+ joinRoom: joinRoomIfNeeded
3781
3871
  };
3782
3872
  }
3783
3873
  var alertVariants = cva(
@@ -3978,12 +4068,14 @@ function OrganifyChat({
3978
4068
  members: chat.roomMembers,
3979
4069
  currentUserId: userId,
3980
4070
  permissions: chat.permissions,
4071
+ workspaceMembers: stableWorkspaceMembers,
3981
4072
  onClose: () => setManagementOpen(false),
3982
4073
  onUpdateRoom: chat.updateRoom,
3983
4074
  onArchiveRoom: chat.archiveRoom,
3984
4075
  onLeaveRoom: chat.leaveRoom,
3985
4076
  onRemoveMember: chat.removeMember,
3986
- onUpdateMemberRole: chat.updateMemberRole
4077
+ onUpdateMemberRole: chat.updateMemberRole,
4078
+ onAddMember: chat.addMember
3987
4079
  }
3988
4080
  ) })
3989
4081
  ] }),
@@ -4038,12 +4130,14 @@ function OrganifyChat({
4038
4130
  members: chat.roomMembers,
4039
4131
  currentUserId: userId,
4040
4132
  permissions: chat.permissions,
4133
+ workspaceMembers: stableWorkspaceMembers,
4041
4134
  onClose: handleCloseManagement,
4042
4135
  onUpdateRoom: chat.updateRoom,
4043
4136
  onArchiveRoom: chat.archiveRoom,
4044
4137
  onLeaveRoom: chat.leaveRoom,
4045
4138
  onRemoveMember: chat.removeMember,
4046
- onUpdateMemberRole: chat.updateMemberRole
4139
+ onUpdateMemberRole: chat.updateMemberRole,
4140
+ onAddMember: chat.addMember
4047
4141
  }
4048
4142
  ) })
4049
4143
  ] })
@@ -4749,5 +4843,5 @@ function useAiInline({ gatewayUrl, workspaceId, projectId }) {
4749
4843
  }
4750
4844
 
4751
4845
  export { AiChatSidebar, Alert, Button, ChatMessages, ChatSidebar, CommandBar, CreateRoomDialog, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, InlineAiButton, Input, Label, MOCK_PROJECTS, MOCK_USERS, MentionPopover, MessageBubble, MessageInput, OrgLoader, OrgLoaderInline, OrganifyChat, ResponsiveDialog, RoomManagementPanel, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, TypingIndicatorMock, UserAvatar, UserDisplayName, alertVariants, buttonVariants, generateAutoReplies, getMockMentionOptions, getRoomPermissions, inputVariants, invalidateUserCache, orgLoaderVariants, resolveUser, seedUserCache, typingIndicator, useAiInline, useChat, useResolvedUser };
4752
- //# sourceMappingURL=chunk-V3UZIPZA.js.map
4753
- //# sourceMappingURL=chunk-V3UZIPZA.js.map
4846
+ //# sourceMappingURL=chunk-5VC6DBVW.js.map
4847
+ //# sourceMappingURL=chunk-5VC6DBVW.js.map