@myrjfa/ui 1.0.8 → 1.0.10

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.
Files changed (161) hide show
  1. package/dist/chat/ChatMain.d.ts +9 -0
  2. package/dist/chat/ChatMain.d.ts.map +1 -0
  3. package/dist/chat/ChatMain.js +272 -0
  4. package/dist/chat/ChatSidebar.d.ts +13 -0
  5. package/dist/chat/ChatSidebar.d.ts.map +1 -0
  6. package/dist/chat/ChatSidebar.js +23 -0
  7. package/dist/chat/ConversationList.d.ts +12 -0
  8. package/dist/chat/ConversationList.d.ts.map +1 -0
  9. package/dist/chat/ConversationList.js +70 -0
  10. package/dist/chat/ExpensePanel.d.ts +11 -0
  11. package/dist/chat/ExpensePanel.d.ts.map +1 -0
  12. package/dist/chat/ExpensePanel.js +235 -0
  13. package/dist/chat/MessageBubble.d.ts +18 -0
  14. package/dist/chat/MessageBubble.d.ts.map +1 -0
  15. package/dist/chat/MessageBubble.js +284 -0
  16. package/dist/chat/MessageInput.d.ts +21 -0
  17. package/dist/chat/MessageInput.d.ts.map +1 -0
  18. package/dist/chat/MessageInput.js +252 -0
  19. package/dist/chat/MilestonePanel.d.ts +11 -0
  20. package/dist/chat/MilestonePanel.d.ts.map +1 -0
  21. package/dist/chat/MilestonePanel.js +173 -0
  22. package/dist/chat/components/ParticipantSelector.d.ts +11 -0
  23. package/dist/chat/components/ParticipantSelector.d.ts.map +1 -0
  24. package/dist/chat/components/ParticipantSelector.js +17 -0
  25. package/dist/chat/components/SplitInputs.d.ts +11 -0
  26. package/dist/chat/components/SplitInputs.d.ts.map +1 -0
  27. package/dist/chat/components/SplitInputs.js +83 -0
  28. package/dist/chat/components/SplitTypeSelector.d.ts +8 -0
  29. package/dist/chat/components/SplitTypeSelector.d.ts.map +1 -0
  30. package/dist/chat/components/SplitTypeSelector.js +13 -0
  31. package/dist/chat/dialogs/AddMemberDialog.d.ts +10 -0
  32. package/dist/chat/dialogs/AddMemberDialog.d.ts.map +1 -0
  33. package/dist/chat/dialogs/AddMemberDialog.js +50 -0
  34. package/dist/chat/dialogs/CameraDialog.d.ts +8 -0
  35. package/dist/chat/dialogs/CameraDialog.d.ts.map +1 -0
  36. package/dist/chat/dialogs/CameraDialog.js +80 -0
  37. package/dist/chat/dialogs/CreatePollDialog.d.ts +8 -0
  38. package/dist/chat/dialogs/CreatePollDialog.d.ts.map +1 -0
  39. package/dist/chat/dialogs/CreatePollDialog.js +38 -0
  40. package/dist/chat/dialogs/ExpenseAnalyticsDialog.d.ts +19 -0
  41. package/dist/chat/dialogs/ExpenseAnalyticsDialog.d.ts.map +1 -0
  42. package/dist/chat/dialogs/ExpenseAnalyticsDialog.js +52 -0
  43. package/dist/chat/dialogs/GroupInfoDialog.d.ts +10 -0
  44. package/dist/chat/dialogs/GroupInfoDialog.d.ts.map +1 -0
  45. package/dist/chat/dialogs/GroupInfoDialog.js +71 -0
  46. package/dist/chat/dialogs/GroupInfoViewDialog.d.ts +9 -0
  47. package/dist/chat/dialogs/GroupInfoViewDialog.d.ts.map +1 -0
  48. package/dist/chat/dialogs/GroupInfoViewDialog.js +15 -0
  49. package/dist/chat/dialogs/NewChatDialog.d.ts +2 -0
  50. package/dist/chat/dialogs/NewChatDialog.d.ts.map +1 -0
  51. package/dist/chat/dialogs/NewChatDialog.js +63 -0
  52. package/dist/chat/dialogs/NewGroupDialog.d.ts +2 -0
  53. package/dist/chat/dialogs/NewGroupDialog.d.ts.map +1 -0
  54. package/dist/chat/dialogs/NewGroupDialog.js +69 -0
  55. package/dist/chat/dialogs/ParticipantsDialog.d.ts +11 -0
  56. package/dist/chat/dialogs/ParticipantsDialog.d.ts.map +1 -0
  57. package/dist/chat/dialogs/ParticipantsDialog.js +93 -0
  58. package/dist/chat/dialogs/UserProfileDialog.d.ts +10 -0
  59. package/dist/chat/dialogs/UserProfileDialog.d.ts.map +1 -0
  60. package/dist/chat/dialogs/UserProfileDialog.js +100 -0
  61. package/dist/chat/index.d.ts +8 -0
  62. package/dist/chat/index.d.ts.map +1 -0
  63. package/dist/chat/index.js +7 -0
  64. package/dist/dashboard/action-buttons.js +4 -5
  65. package/dist/dashboard/app-sidebar.js +46 -128
  66. package/dist/dashboard/card-header.js +1 -2
  67. package/dist/dashboard/dashboard-components.js +3 -5
  68. package/dist/dashboard/data-table.d.ts +18 -18
  69. package/dist/dashboard/data-table.js +54 -66
  70. package/dist/dashboard/nav-main.js +4 -5
  71. package/dist/dashboard/nav-secondary.js +2 -25
  72. package/dist/dashboard/stats-card.js +1 -2
  73. package/dist/dashboard/table.js +8 -54
  74. package/dist/header/mobile-nav-item.js +1 -2
  75. package/dist/header/mobile-navbar.js +8 -9
  76. package/dist/header/nav-item.d.ts +2 -1
  77. package/dist/header/nav-item.d.ts.map +1 -1
  78. package/dist/header/nav-item.js +2 -3
  79. package/dist/index.d.ts +57 -56
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +57 -57
  82. package/dist/main/footer.js +5 -9
  83. package/dist/main/navbar.d.ts.map +1 -1
  84. package/dist/main/navbar.js +53 -136
  85. package/dist/main/paginationBar.js +12 -14
  86. package/dist/main/portfolio/portfolioItem.js +1 -2
  87. package/dist/main/portfolio/portfolioPopup.js +16 -17
  88. package/dist/main/review-page.d.ts.map +1 -1
  89. package/dist/main/review-page.js +131 -198
  90. package/dist/misc/accordion.js +8 -34
  91. package/dist/misc/alert-dialog.js +22 -55
  92. package/dist/misc/alert.js +7 -32
  93. package/dist/misc/alertdialogclaude.js +50 -125
  94. package/dist/misc/avatar.js +6 -31
  95. package/dist/misc/badge.d.ts +8 -8
  96. package/dist/misc/badge.js +4 -27
  97. package/dist/misc/button.d.ts +9 -9
  98. package/dist/misc/button.js +4 -27
  99. package/dist/misc/card.js +12 -40
  100. package/dist/misc/carousel.js +37 -61
  101. package/dist/misc/checkbox.js +2 -25
  102. package/dist/misc/command.d.ts +3 -1
  103. package/dist/misc/command.d.ts.map +1 -1
  104. package/dist/misc/command.js +18 -49
  105. package/dist/misc/datepicker.js +66 -76
  106. package/dist/misc/dialog.js +20 -52
  107. package/dist/misc/dropdown-menu.js +30 -67
  108. package/dist/misc/faq.js +2 -3
  109. package/dist/misc/file-preview.js +43 -97
  110. package/dist/misc/filter.d.ts.map +1 -1
  111. package/dist/misc/filter.js +18 -21
  112. package/dist/misc/filterBar.js +136 -160
  113. package/dist/misc/form.d.ts +1 -1
  114. package/dist/misc/form.d.ts.map +1 -1
  115. package/dist/misc/form.js +39 -60
  116. package/dist/misc/image-cropper.d.ts +10 -0
  117. package/dist/misc/image-cropper.d.ts.map +1 -0
  118. package/dist/misc/image-cropper.js +44 -0
  119. package/dist/misc/input.js +2 -25
  120. package/dist/misc/label.js +2 -25
  121. package/dist/misc/line-carousel.js +3 -13
  122. package/dist/misc/multiImageUploader.d.ts +8 -8
  123. package/dist/misc/multiImageUploader.js +10 -11
  124. package/dist/misc/newsLetter.js +18 -78
  125. package/dist/misc/notification.d.ts +1 -2
  126. package/dist/misc/notification.d.ts.map +1 -1
  127. package/dist/misc/notification.js +89 -125
  128. package/dist/misc/pagination.js +16 -45
  129. package/dist/misc/password-eye.js +2 -3
  130. package/dist/misc/popover.d.ts.map +1 -1
  131. package/dist/misc/popover.js +8 -34
  132. package/dist/misc/progress.js +2 -25
  133. package/dist/misc/scroll-area.js +5 -29
  134. package/dist/misc/searchBar.js +27 -31
  135. package/dist/misc/searchBox.js +18 -22
  136. package/dist/misc/select.d.ts.map +1 -1
  137. package/dist/misc/select.js +22 -54
  138. package/dist/misc/separator.js +2 -25
  139. package/dist/misc/share.js +48 -104
  140. package/dist/misc/sheet.js +21 -53
  141. package/dist/misc/sidebar.d.ts +69 -69
  142. package/dist/misc/sidebar.js +112 -153
  143. package/dist/misc/skeleton.d.ts +2 -2
  144. package/dist/misc/skeleton.js +6 -29
  145. package/dist/misc/slider.js +3 -26
  146. package/dist/misc/starRating.js +5 -15
  147. package/dist/misc/stateCard.js +7 -8
  148. package/dist/misc/switch.d.ts +5 -0
  149. package/dist/misc/switch.d.ts.map +1 -0
  150. package/dist/misc/switch.js +8 -0
  151. package/dist/misc/tabs.js +8 -34
  152. package/dist/misc/textarea.js +2 -25
  153. package/dist/misc/tile.js +31 -92
  154. package/dist/misc/tooltip.js +8 -34
  155. package/dist/profile/socialLink.d.ts +1 -1
  156. package/dist/profile/socialLink.d.ts.map +1 -1
  157. package/dist/profile/socialLink.js +53 -69
  158. package/dist/utils/permissions.d.ts +7 -0
  159. package/dist/utils/permissions.d.ts.map +1 -0
  160. package/dist/utils/permissions.js +38 -0
  161. package/package.json +8 -5
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import type { User as UserType } from "@myrjfa/state/dist/lib/models/user";
3
+ interface AddMemberDialogProps {
4
+ selectedUsers: UserType[];
5
+ setSelectedUsers: React.Dispatch<React.SetStateAction<UserType[]>>;
6
+ excludeUsernames?: string[];
7
+ }
8
+ export declare function AddMemberDialog({ selectedUsers, setSelectedUsers, excludeUsernames }: AddMemberDialogProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=AddMemberDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddMemberDialog.d.ts","sourceRoot":"","sources":["../../../src/chat/dialogs/AddMemberDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAMxC,OAAO,KAAK,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAE3E,UAAU,oBAAoB;IAC1B,aAAa,EAAE,QAAQ,EAAE,CAAC;IAC1B,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;IAClE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,wBAAgB,eAAe,CAAC,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAqB,EAAE,EAAE,oBAAoB,2CAmF/G"}
@@ -0,0 +1,50 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Input } from "../../misc/input";
5
+ import { Button } from "../../misc/button";
6
+ import { User, Loader2, X } from "lucide-react";
7
+ import { userDetails } from "@myrjfa/state/dist/lib/actions/actions";
8
+ import { Avatar, AvatarFallback, AvatarImage } from "../../misc/avatar";
9
+ export function AddMemberDialog({ selectedUsers, setSelectedUsers, excludeUsernames = [] }) {
10
+ const [query, setQuery] = useState("");
11
+ const [results, setResults] = useState([]);
12
+ const [loading, setLoading] = useState(false);
13
+ async function handleSearch(e) {
14
+ e.preventDefault();
15
+ if (!query.trim())
16
+ return;
17
+ setLoading(true);
18
+ try {
19
+ const res = await userDetails(query, "user");
20
+ if (res.error) {
21
+ console.error("user not found:", res.error);
22
+ setResults([]);
23
+ }
24
+ else {
25
+ if (res.user && !excludeUsernames.includes(res.user.username || "") && !selectedUsers.some(u => res.user ? u.username == res.user.username : false)) {
26
+ setResults([res.user]);
27
+ }
28
+ }
29
+ }
30
+ catch (err) {
31
+ console.error(err);
32
+ }
33
+ finally {
34
+ setLoading(false);
35
+ }
36
+ }
37
+ function toggleUser(user) {
38
+ if (selectedUsers.find(u => u.username === user.username)) {
39
+ setSelectedUsers(prev => prev.filter(u => u.username !== user.username));
40
+ setQuery("");
41
+ setResults([]);
42
+ }
43
+ else {
44
+ setSelectedUsers(prev => [...prev, user]);
45
+ setQuery("");
46
+ setResults([]);
47
+ }
48
+ }
49
+ return (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "flex flex-wrap gap-2", children: selectedUsers.map(user => (_jsxs("div", { className: "flex items-center gap-1 bg-indigo-100 text-indigo-700 text-xs px-2 py-1 rounded-full", children: [_jsx("span", { children: user.firstName }), _jsx(X, { className: "w-3 h-3 cursor-pointer", onClick: () => toggleUser(user) })] }, user.username))) }), _jsxs("form", { onSubmit: handleSearch, className: "flex gap-2", children: [_jsx(Input, { placeholder: "Add members by email, contact or username...", value: query, onChange: (e) => setQuery(e.target.value) }), _jsx(Button, { type: "submit", size: "sm", disabled: loading, children: loading ? _jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : "Find" })] }), _jsx("div", { className: "flex flex-col gap-2 max-h-[200px] overflow-y-auto", children: results.map((user) => (_jsx("div", { className: "flex items-center justify-between p-2 hover:bg-accent rounded-md cursor-pointer", onClick: () => toggleUser(user), children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Avatar, { className: "h-8 w-8", children: [_jsx(AvatarImage, { src: user.profilePic }), _jsx(AvatarFallback, { children: _jsx(User, { className: "h-4 w-4" }) })] }), _jsxs("div", { children: [_jsxs("p", { className: "text-sm font-medium", children: [user.firstName, " ", user.lastName] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["@", user.username] })] })] }) }, user.username))) })] }));
50
+ }
@@ -0,0 +1,8 @@
1
+ interface CameraDialogProps {
2
+ open: boolean;
3
+ onOpenChange: (open: boolean) => void;
4
+ onCapture: (file: File) => void;
5
+ }
6
+ export declare function CameraDialog({ open, onOpenChange, onCapture }: CameraDialogProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=CameraDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CameraDialog.d.ts","sourceRoot":"","sources":["../../../src/chat/dialogs/CameraDialog.tsx"],"names":[],"mappings":"AAOA,UAAU,iBAAiB;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;CACnC;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,EAAE,iBAAiB,2CA2HhF"}
@@ -0,0 +1,80 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useRef, useState, useEffect } from "react";
4
+ import { Dialog, DialogContent } from "../../misc/dialog";
5
+ import { Button } from "../../misc/button";
6
+ import { RefreshCw, Check } from "lucide-react";
7
+ export function CameraDialog({ open, onOpenChange, onCapture }) {
8
+ const videoRef = useRef(null);
9
+ const canvasRef = useRef(null);
10
+ const [stream, setStream] = useState(null);
11
+ const [capturedImage, setCapturedImage] = useState(null);
12
+ const [loading, setLoading] = useState(false);
13
+ useEffect(() => {
14
+ if (open) {
15
+ startCamera();
16
+ }
17
+ else {
18
+ stopCamera();
19
+ setCapturedImage(null);
20
+ }
21
+ return () => stopCamera();
22
+ }, [open]);
23
+ const startCamera = async () => {
24
+ try {
25
+ setLoading(true);
26
+ const mediaStream = await navigator.mediaDevices.getUserMedia({
27
+ video: { facingMode: "user" }
28
+ });
29
+ setStream(mediaStream);
30
+ if (videoRef.current) {
31
+ videoRef.current.srcObject = mediaStream;
32
+ }
33
+ setLoading(false);
34
+ }
35
+ catch (err) {
36
+ console.error("Error accessing camera:", err);
37
+ setLoading(false);
38
+ alert("Could not access camera");
39
+ onOpenChange(false);
40
+ }
41
+ };
42
+ const stopCamera = () => {
43
+ if (stream) {
44
+ stream.getTracks().forEach(track => track.stop());
45
+ setStream(null);
46
+ }
47
+ };
48
+ const handleCapture = () => {
49
+ if (videoRef.current && canvasRef.current) {
50
+ const video = videoRef.current;
51
+ const canvas = canvasRef.current;
52
+ canvas.width = video.videoWidth;
53
+ canvas.height = video.videoHeight;
54
+ const ctx = canvas.getContext("2d");
55
+ if (ctx) {
56
+ // Flip horizontally if facing user mode (optional, but good for selfies)
57
+ // ctx.translate(canvas.width, 0);
58
+ // ctx.scale(-1, 1);
59
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
60
+ const dataUrl = canvas.toDataURL("image/jpeg");
61
+ setCapturedImage(dataUrl);
62
+ }
63
+ }
64
+ };
65
+ const handleRetake = () => {
66
+ setCapturedImage(null);
67
+ };
68
+ const handleConfirm = () => {
69
+ if (capturedImage) {
70
+ fetch(capturedImage)
71
+ .then(res => res.blob())
72
+ .then(blob => {
73
+ const file = new File([blob], `camera_${Date.now()}.jpg`, { type: "image/jpeg" });
74
+ onCapture(file);
75
+ onOpenChange(false);
76
+ });
77
+ }
78
+ };
79
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-md p-0 overflow-hidden bg-black border-gray-800", children: [_jsxs("div", { className: "relative aspect-[3/4] sm:aspect-video bg-black flex items-center justify-center", children: [!capturedImage && (_jsx("video", { ref: videoRef, autoPlay: true, playsInline: true, className: "w-full h-full object-cover" })), capturedImage && (_jsx("img", { src: capturedImage, alt: "Captured", className: "w-full h-full object-contain" })), _jsx("canvas", { ref: canvasRef, className: "hidden" })] }), _jsx("div", { className: "p-4 flex items-center justify-center gap-4 bg-gray-900 border-t border-gray-800", children: !capturedImage ? (_jsx(Button, { onClick: handleCapture, className: "h-16 w-16 rounded-full border-4 border-white bg-transparent p-1 hover:bg-white/10", children: _jsx("div", { className: "h-full w-full rounded-full bg-white" }) })) : (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "ghost", className: "text-white hover:bg-gray-800", onClick: handleRetake, children: [_jsx(RefreshCw, { className: "w-5 h-5 mr-2" }), "Retake"] }), _jsxs(Button, { className: "bg-white text-black hover:bg-gray-200", onClick: handleConfirm, children: [_jsx(Check, { className: "w-5 h-5 mr-2" }), "Send Photo"] })] })) })] }) }));
80
+ }
@@ -0,0 +1,8 @@
1
+ interface CreatePollDialogProps {
2
+ open: boolean;
3
+ onOpenChange: (open: boolean) => void;
4
+ onCreate: (question: string, options: string[], allowMultiple: boolean) => void;
5
+ }
6
+ export declare function CreatePollDialog({ open, onOpenChange, onCreate }: CreatePollDialogProps): import("react/jsx-runtime").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=CreatePollDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CreatePollDialog.d.ts","sourceRoot":"","sources":["../../../src/chat/dialogs/CreatePollDialog.tsx"],"names":[],"mappings":"AAQA,UAAU,qBAAqB;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;CACnF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,qBAAqB,2CAmHvF"}
@@ -0,0 +1,38 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../../misc/dialog";
5
+ import { Button } from "../../misc/button";
6
+ import { Input } from "../../misc/input";
7
+ import { Plus, Trash2, ChartBar } from "lucide-react";
8
+ export function CreatePollDialog({ open, onOpenChange, onCreate }) {
9
+ const [question, setQuestion] = useState("");
10
+ const [options, setOptions] = useState(["", ""]);
11
+ const [allowMultiple, setAllowMultiple] = useState(false);
12
+ const addOption = () => {
13
+ if (options.length < 10) {
14
+ setOptions([...options, ""]);
15
+ }
16
+ };
17
+ const removeOption = (index) => {
18
+ if (options.length > 2) {
19
+ setOptions(options.filter((_, i) => i !== index));
20
+ }
21
+ };
22
+ const updateOption = (index, value) => {
23
+ const next = [...options];
24
+ next[index] = value;
25
+ setOptions(next);
26
+ };
27
+ const handleCreate = () => {
28
+ const validOptions = options.map(o => o.trim()).filter(o => o.length > 0);
29
+ if (!question.trim() || validOptions.length < 2)
30
+ return;
31
+ onCreate(question.trim(), validOptions, allowMultiple);
32
+ setQuestion("");
33
+ setOptions(["", ""]);
34
+ setAllowMultiple(false);
35
+ onOpenChange(false);
36
+ };
37
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-[425px]", children: [_jsx(DialogHeader, { children: _jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(ChartBar, { className: "w-5 h-5 text-indigo-500" }), "Create Poll"] }) }), _jsxs("div", { className: "space-y-4 py-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: "Question" }), _jsx(Input, { placeholder: "Ask a question...", value: question, onChange: (e) => setQuestion(e.target.value) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: "Options" }), _jsx("div", { className: "space-y-2 max-h-[200px] overflow-y-auto pr-1", children: options.map((opt, i) => (_jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { placeholder: `Option ${i + 1}`, value: opt, onChange: (e) => updateOption(i, e.target.value) }), options.length > 2 && (_jsx(Button, { variant: "ghost", size: "icon", className: "text-red-500 hover:text-red-700 hover:bg-red-50 shrink-0", onClick: () => removeOption(i), children: _jsx(Trash2, { className: "w-4 h-4" }) }))] }, i))) }), options.length < 10 && (_jsxs(Button, { variant: "outline", size: "sm", className: "w-full gap-2 border-dashed", onClick: addOption, children: [_jsx(Plus, { className: "w-4 h-4" }), "Add Option"] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "checkbox", id: "allowMultiple", checked: allowMultiple, onChange: (e) => setAllowMultiple(e.target.checked), className: "w-4 h-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" }), _jsx("label", { htmlFor: "allowMultiple", className: "text-sm text-gray-600", children: "Allow selecting multiple answers" })] })] }), _jsxs("div", { className: "flex justify-end gap-3 mt-2", children: [_jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsx(Button, { onClick: handleCreate, disabled: !question.trim() || options.filter(o => o.trim()).length < 2, className: "bg-indigo-600 hover:bg-indigo-700 text-white", children: "Create Poll" })] })] }) }));
38
+ }
@@ -0,0 +1,19 @@
1
+ interface ExpenseAnalyticsDialogProps {
2
+ conversationId: string;
3
+ open: boolean;
4
+ onClose: () => void;
5
+ }
6
+ export interface GroupBalance {
7
+ balances: {
8
+ userId: string;
9
+ netBalance: number;
10
+ }[];
11
+ settlements: {
12
+ from: string;
13
+ to: string;
14
+ amount: number;
15
+ }[];
16
+ }
17
+ export declare function ExpenseAnalyticsDialog({ conversationId, open, onClose }: ExpenseAnalyticsDialogProps): import("react/jsx-runtime").JSX.Element;
18
+ export {};
19
+ //# sourceMappingURL=ExpenseAnalyticsDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpenseAnalyticsDialog.d.ts","sourceRoot":"","sources":["../../../src/chat/dialogs/ExpenseAnalyticsDialog.tsx"],"names":[],"mappings":"AAQA,UAAU,2BAA2B;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAYD,MAAM,WAAW,YAAY;IACzB,QAAQ,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAA;KACrB,EAAE,CAAC;IACJ,WAAW,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAA;KACjB,EAAE,CAAA;CACN;AAED,wBAAgB,sBAAsB,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,2BAA2B,2CAyMpG"}
@@ -0,0 +1,52 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from "react";
4
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../../misc/dialog";
5
+ import { chatApi } from "@myrjfa/state/dist/lib/actions/chat";
6
+ import { BarChart3, TrendingUp, Users, PieChart } from "lucide-react";
7
+ export function ExpenseAnalyticsDialog({ conversationId, open, onClose }) {
8
+ const [analytics, setAnalytics] = useState(null);
9
+ const [loading, setLoading] = useState(false);
10
+ useEffect(() => {
11
+ if (open && conversationId) {
12
+ fetchAnalytics();
13
+ }
14
+ }, [open, conversationId]);
15
+ const fetchAnalytics = async () => {
16
+ setLoading(true);
17
+ try {
18
+ const data = await chatApi.getExpenseAnalytics(conversationId);
19
+ setAnalytics(data);
20
+ }
21
+ catch (error) {
22
+ console.error("Failed to fetch analytics:", error);
23
+ }
24
+ finally {
25
+ setLoading(false);
26
+ }
27
+ };
28
+ const getCategoryColor = (index) => {
29
+ const colors = [
30
+ "bg-teal-500", "bg-cyan-500", "bg-blue-500", "bg-purple-500",
31
+ "bg-pink-500", "bg-orange-500", "bg-yellow-500", "bg-green-500"
32
+ ];
33
+ return colors[index % colors.length];
34
+ };
35
+ return (_jsx(Dialog, { open: open, onOpenChange: onClose, children: _jsxs(DialogContent, { className: "sm:max-w-2xl mt-15 max-h-[90vh] overflow-y-auto", children: [_jsx(DialogHeader, { children: _jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(BarChart3, { className: "w-5 h-5 text-teal-500" }), "Expense Analytics"] }) }), loading ? (_jsx("div", { className: "flex justify-center py-8", children: _jsx("div", { className: "animate-spin w-6 h-6 border-2 border-teal-500 border-t-transparent rounded-full" }) })) : !analytics || analytics.count === 0 ? (_jsxs("div", { className: "text-center py-8 text-gray-400", children: [_jsx(PieChart, { className: "w-12 h-12 mx-auto mb-3 opacity-50" }), _jsx("p", { children: "No expenses to analyze yet" })] })) : (_jsxs("div", { className: "space-y-6 py-2", children: [_jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "bg-gradient-to-br from-teal-50 to-cyan-50 dark:from-teal-900/20 dark:to-cyan-900/20 rounded-xl p-3 text-center", children: [_jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mb-1", children: "Total" }), _jsxs("p", { className: "text-lg font-bold text-teal-700 dark:text-teal-300", children: ["\u20B9", analytics.total.toFixed(0)] })] }), _jsxs("div", { className: "bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20 rounded-xl p-3 text-center", children: [_jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mb-1", children: "Count" }), _jsx("p", { className: "text-lg font-bold text-blue-700 dark:text-blue-300", children: analytics.count })] }), _jsxs("div", { className: "bg-gradient-to-br from-purple-50 to-pink-50 dark:from-purple-900/20 dark:to-pink-900/20 rounded-xl p-3 text-center", children: [_jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mb-1", children: "Average" }), _jsxs("p", { className: "text-lg font-bold text-purple-700 dark:text-purple-300", children: ["\u20B9", analytics.averageAmount.toFixed(0)] })] })] }), Object.keys(analytics.byCategory).length > 0 && (_jsxs("div", { children: [_jsxs("h4", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3 flex items-center gap-2", children: [_jsx(PieChart, { className: "w-4 h-4" }), "By Category"] }), _jsx("div", { className: "space-y-2", children: Object.entries(analytics.byCategory)
36
+ .sort(([, a], [, b]) => b - a)
37
+ .map(([category, amount], index) => {
38
+ const percentage = (amount / analytics.total) * 100;
39
+ return (_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex justify-between text-xs", children: [_jsx("span", { className: "text-gray-600 dark:text-gray-400", children: category }), _jsxs("span", { className: "font-medium text-gray-700 dark:text-gray-300", children: ["\u20B9", amount.toFixed(0), " (", percentage.toFixed(0), "%)"] })] }), _jsx("div", { className: "w-full h-2 bg-gray-100 dark:bg-gray-800 rounded-full overflow-hidden", children: _jsx("div", { className: `h-full ${getCategoryColor(index)} rounded-full transition-all duration-500`, style: { width: `${percentage}%` } }) })] }, category));
40
+ }) })] })), Object.keys(analytics.byPerson).length > 0 && (_jsxs("div", { children: [_jsxs("h4", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3 flex items-center gap-2", children: [_jsx(Users, { className: "w-4 h-4" }), "Expense per person"] }), _jsx("div", { className: "space-y-2", children: Object.entries(analytics.byPerson)
41
+ .sort(([, a], [, b]) => b - a)
42
+ .map(([person, amount]) => {
43
+ const percentage = (amount / analytics.total) * 100;
44
+ return (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "w-8 h-8 bg-gradient-to-br from-teal-400 to-cyan-500 rounded-full flex items-center justify-center text-white text-xs font-bold flex-shrink-0", children: person.charAt(0).toUpperCase() }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-xs text-gray-600 dark:text-gray-400 truncate", children: person }), _jsx("div", { className: "w-full h-1.5 bg-gray-100 dark:bg-gray-800 rounded-full overflow-hidden mt-1", children: _jsx("div", { className: "h-full bg-gradient-to-r from-teal-400 to-cyan-500 rounded-full transition-all duration-500", style: { width: `${percentage}%` } }) })] }), _jsxs("span", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300 flex-shrink-0", children: ["\u20B9", amount.toFixed(0)] })] }, person));
45
+ }) })] })), Object.keys(analytics.byCategoryForUser).length > 0 && (_jsxs("div", { children: [_jsxs("h4", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3 flex items-center gap-2", children: [_jsx(BarChart3, { className: "w-4 h-4" }), "Your Expenses by Category"] }), _jsx("div", { className: "bg-gray-50 dark:bg-gray-800/50 rounded-lg p-3", children: _jsx("div", { className: "space-y-1.5", children: Object.entries(analytics.byCategoryForUser)
46
+ .sort(([, a], [, b]) => b - a)
47
+ .map(([category, amount], index) => {
48
+ const userTotal = Object.values(analytics.byCategoryForUser).reduce((sum, val) => sum + val, 0);
49
+ const percentage = (amount / userTotal) * 100;
50
+ return (_jsxs("div", { className: "text-xs", children: [_jsxs("div", { className: "flex justify-between mb-1", children: [_jsx("span", { className: "text-gray-600 dark:text-gray-400", children: category }), _jsxs("span", { className: "font-medium text-gray-700 dark:text-gray-300", children: ["\u20B9", amount.toFixed(0), " (", percentage.toFixed(0), "%)"] })] }), _jsx("div", { className: "w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-full overflow-hidden", children: _jsx("div", { className: `h-full ${getCategoryColor(index)} rounded-full`, style: { width: `${percentage}%` } }) })] }, category));
51
+ }) }) })] })), analytics.groupBalances && (_jsxs("div", { children: [_jsxs("h4", { className: "text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3 flex items-center gap-2", children: [_jsx(TrendingUp, { className: "w-4 h-4" }), "Settlement Balances"] }), analytics.groupBalances.balances && analytics.groupBalances.balances.length > 0 && (_jsxs("div", { className: "bg-gray-50 dark:bg-gray-800/50 rounded-lg p-3 mb-3", children: [_jsx("p", { className: "text-xs font-medium text-gray-600 dark:text-gray-400 mb-2", children: "Individual Balances" }), _jsx("div", { className: "space-y-1.5", children: analytics.groupBalances.balances.map((b) => (_jsxs("div", { className: "flex justify-between text-xs py-1", children: [_jsx("span", { className: "text-gray-600 dark:text-gray-400", children: b.userId }), _jsxs("span", { className: `font-medium ${b.netBalance >= 0 ? "text-green-600" : "text-red-500"}`, children: [b.netBalance >= 0 ? "+" : "", "\u20B9", b.netBalance.toFixed(2)] })] }, b.userId))) })] }))] }))] }))] }) }));
52
+ }
@@ -0,0 +1,10 @@
1
+ import type { Conversation } from "@myrjfa/state/dist/lib/models/chat";
2
+ interface GroupInfoDialogProps {
3
+ open: boolean;
4
+ onOpenChange: (open: boolean) => void;
5
+ conversation: Conversation;
6
+ onUpdate: () => void;
7
+ }
8
+ export declare function GroupInfoDialog({ open, onOpenChange, conversation, onUpdate }: GroupInfoDialogProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=GroupInfoDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GroupInfoDialog.d.ts","sourceRoot":"","sources":["../../../src/chat/dialogs/GroupInfoDialog.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAKvE,UAAU,oBAAoB;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,eAAe,CAAC,EAC5B,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,QAAQ,EACX,EAAE,oBAAoB,2CA4ItB"}
@@ -0,0 +1,71 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "../../misc/dialog";
5
+ import { Button } from "../../misc/button";
6
+ import { Input } from "../../misc/input";
7
+ import { Textarea } from "../../misc/textarea";
8
+ import { chatApi } from "@myrjfa/state/dist/lib/actions/chat";
9
+ import { Avatar, AvatarFallback, AvatarImage } from "../../misc/avatar";
10
+ import { Users, Camera, Loader2 } from "lucide-react";
11
+ import { Switch } from "../../misc/switch";
12
+ export function GroupInfoDialog({ open, onOpenChange, conversation, onUpdate }) {
13
+ var _a, _b;
14
+ const [name, setName] = useState(conversation.name || "");
15
+ const [description, setDescription] = useState(conversation.description || "");
16
+ const [imageFile, setImageFile] = useState(null);
17
+ const [previewImage, setPreviewImage] = useState(conversation.image || "");
18
+ const [loading, setLoading] = useState(false);
19
+ const [milestonesEnabled, setMilestonesEnabled] = useState(((_a = conversation.features) === null || _a === void 0 ? void 0 : _a.milestones) || false);
20
+ const [expensesEnabled, setExpensesEnabled] = useState(((_b = conversation.features) === null || _b === void 0 ? void 0 : _b.expenses) || false);
21
+ useEffect(() => {
22
+ var _a, _b;
23
+ setName(conversation.name || "");
24
+ setDescription(conversation.description || "");
25
+ setPreviewImage(conversation.image || "");
26
+ setMilestonesEnabled(((_a = conversation.features) === null || _a === void 0 ? void 0 : _a.milestones) || false);
27
+ setExpensesEnabled(((_b = conversation.features) === null || _b === void 0 ? void 0 : _b.expenses) || false);
28
+ }, [conversation]);
29
+ const handleImageChange = (e) => {
30
+ if (e.target.files && e.target.files[0]) {
31
+ const file = e.target.files[0];
32
+ setImageFile(file);
33
+ setPreviewImage(URL.createObjectURL(file));
34
+ }
35
+ };
36
+ const handleSave = async () => {
37
+ setLoading(true);
38
+ try {
39
+ // If image changed, upload it first (reuse chat upload or dedicated endpoint)
40
+ let imageUrl = conversation.image;
41
+ if (imageFile) {
42
+ const formData = new FormData();
43
+ formData.append("chatFiles", imageFile);
44
+ const res = await chatApi.uploadFiles(formData);
45
+ if (res.data && res.data.files && res.data.files.length > 0) {
46
+ imageUrl = res.data.files[0].location;
47
+ }
48
+ }
49
+ // Update conversation
50
+ await chatApi.updateConversation(conversation._id, {
51
+ name,
52
+ description,
53
+ image: imageUrl,
54
+ features: {
55
+ milestones: milestonesEnabled,
56
+ expenses: expensesEnabled
57
+ }
58
+ });
59
+ onUpdate();
60
+ onOpenChange(false);
61
+ }
62
+ catch (err) {
63
+ console.error("Failed to update group info:", err);
64
+ alert("Failed to update group info");
65
+ }
66
+ finally {
67
+ setLoading(false);
68
+ }
69
+ };
70
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-[425px] mt-15", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "Edit Group Info" }) }), _jsxs("div", { className: "space-y-6 py-4", children: [_jsxs("div", { className: "flex flex-col items-center gap-2", children: [_jsxs("div", { className: "relative group cursor-pointer", children: [_jsxs(Avatar, { className: "w-24 h-24 shadow-md", children: [_jsx(AvatarImage, { src: previewImage }), _jsx(AvatarFallback, { className: "bg-gradient-to-br from-indigo-500 to-purple-600 text-white text-2xl", children: _jsx(Users, { className: "w-10 h-10" }) })] }), _jsxs("label", { className: "absolute inset-0 flex items-center justify-center bg-black/40 text-white opacity-0 group-hover:opacity-100 rounded-full transition-opacity cursor-pointer", children: [_jsx(Camera, { className: "w-8 h-8" }), _jsx("input", { type: "file", className: "hidden", accept: "image/*", onChange: handleImageChange })] })] }), _jsx("p", { className: "text-xs text-gray-500", children: "Click to change group icon" })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: "Group Name" }), _jsx(Input, { value: name, onChange: (e) => setName(e.target.value), placeholder: "Enter group name" })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: "Description" }), _jsx(Textarea, { value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Add a group description (optional)", className: "resize-none", rows: 3 })] }), _jsxs("div", { className: "space-y-3 pt-4 border-t border-gray-200 dark:border-gray-700", children: [_jsx("p", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "Group Features" }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex-1", children: [_jsx("label", { className: "text-sm font-medium", children: "Milestones" }), _jsx("p", { className: "text-xs text-gray-500", children: "Track group tasks and progress" })] }), _jsx(Switch, { checked: milestonesEnabled, onCheckedChange: setMilestonesEnabled })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex-1", children: [_jsx("label", { className: "text-sm font-medium", children: "Expenses" }), _jsx("p", { className: "text-xs text-gray-500", children: "Split costs and track balances" })] }), _jsx(Switch, { checked: expensesEnabled, onCheckedChange: setExpensesEnabled })] })] })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: loading, children: "Cancel" }), _jsxs(Button, { onClick: handleSave, disabled: loading, children: [loading && _jsx(Loader2, { className: "w-4 h-4 mr-2 animate-spin" }), "Save Changes"] })] })] }) }));
71
+ }
@@ -0,0 +1,9 @@
1
+ import type { Conversation } from "@myrjfa/state/dist/lib/models/chat";
2
+ interface GroupInfoViewDialogProps {
3
+ open: boolean;
4
+ onOpenChange: (open: boolean) => void;
5
+ conversation: Conversation;
6
+ }
7
+ export declare function GroupInfoViewDialog({ open, onOpenChange, conversation, }: GroupInfoViewDialogProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
9
+ //# sourceMappingURL=GroupInfoViewDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GroupInfoViewDialog.d.ts","sourceRoot":"","sources":["../../../src/chat/dialogs/GroupInfoViewDialog.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAEvE,UAAU,wBAAwB;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,YAAY,EAAE,YAAY,CAAC;CAC9B;AAED,wBAAgB,mBAAmB,CAAC,EAChC,IAAI,EACJ,YAAY,EACZ,YAAY,GACf,EAAE,wBAAwB,2CAiH1B"}
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "../../misc/dialog";
4
+ import { Avatar, AvatarFallback, AvatarImage } from "../../misc/avatar";
5
+ import { Users, Shield, Calendar } from "lucide-react";
6
+ export function GroupInfoViewDialog({ open, onOpenChange, conversation, }) {
7
+ const admins = conversation.participants.filter(p => p.chatRole === "admin");
8
+ const createdDate = conversation.createdAt ? new Date(conversation.createdAt).toLocaleDateString("en-US", {
9
+ year: "numeric",
10
+ month: "long",
11
+ day: "numeric"
12
+ }) : "Unknown";
13
+ const createdBy = conversation.participants.find(p => p.userId === conversation.createdBy);
14
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "sm:max-w-md mt-15", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Group Information" }), _jsx(DialogDescription, { children: "View group details and members" })] }), _jsxs("div", { className: "space-y-6", children: [_jsx("div", { className: "flex justify-center", children: _jsxs(Avatar, { className: "w-24 h-24", children: [_jsx(AvatarImage, { src: conversation.image }), _jsx(AvatarFallback, { className: "bg-gradient-to-br from-indigo-500 to-purple-600 text-white text-2xl", children: _jsx(Users, { className: "w-8 h-8" }) })] }) }), _jsxs("div", { children: [_jsx("label", { className: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "Group Name" }), _jsx("p", { className: "text-lg font-semibold text-gray-900 dark:text-white mt-1", children: conversation.name })] }), conversation.description && (_jsxs("div", { children: [_jsx("label", { className: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "Description" }), _jsx("p", { className: "text-gray-700 dark:text-gray-300 mt-1", children: conversation.description })] })), _jsxs("div", { children: [_jsxs("label", { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [_jsx(Users, { className: "w-4 h-4" }), "Members"] }), _jsxs("p", { className: "text-gray-900 dark:text-white mt-1", children: [conversation.participants.length, " ", conversation.participants.length === 1 ? "member" : "members"] })] }), _jsxs("div", { children: [_jsxs("label", { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [_jsx(Calendar, { className: "w-4 h-4" }), "Created"] }), _jsx("p", { className: "text-gray-900 dark:text-white mt-1", children: createdDate })] }), createdBy && (_jsxs("div", { children: [_jsx("label", { className: "text-sm font-medium text-gray-500 dark:text-gray-400", children: "Created by" }), _jsxs("div", { className: "flex items-center gap-3 mt-2", children: [_jsxs(Avatar, { className: "w-8 h-8", children: [_jsx(AvatarImage, { src: createdBy.profilePic }), _jsx(AvatarFallback, { className: "bg-indigo-500 text-white text-xs", children: createdBy.userId.charAt(0).toUpperCase() })] }), _jsx("span", { className: "text-gray-900 dark:text-white", children: createdBy.userId })] })] })), admins.length > 0 && (_jsxs("div", { children: [_jsxs("label", { className: "text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center gap-2", children: [_jsx(Shield, { className: "w-4 h-4" }), "Admins"] }), _jsx("div", { className: "space-y-2 mt-2", children: admins.map((admin) => (_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Avatar, { className: "w-8 h-8", children: [_jsx(AvatarImage, { src: admin.profilePic }), _jsx(AvatarFallback, { className: "bg-purple-500 text-white text-xs", children: admin.userId.charAt(0).toUpperCase() })] }), _jsx("span", { className: "text-gray-900 dark:text-white", children: admin.userId })] }, admin.userId))) })] }))] })] }) }));
15
+ }
@@ -0,0 +1,2 @@
1
+ export declare function NewChatDialog(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=NewChatDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NewChatDialog.d.ts","sourceRoot":"","sources":["../../../src/chat/dialogs/NewChatDialog.tsx"],"names":[],"mappings":"AAcA,wBAAgB,aAAa,4CA2G5B"}
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from "react";
4
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "../../misc/dialog";
5
+ import { Button } from "../../misc/button";
6
+ import { Input } from "../../misc/input";
7
+ import { MessageCircle, Search, User, Loader2 } from "lucide-react";
8
+ import { userDetails } from "@myrjfa/state/dist/lib/actions/actions"; // Check if these are exported from index
9
+ import { Avatar, AvatarFallback, AvatarImage } from "../../misc/avatar";
10
+ import { useRouter } from "next/navigation";
11
+ import { userApi } from "@myrjfa/state/dist/lib/actions/user";
12
+ export function NewChatDialog() {
13
+ const [open, setOpen] = useState(false);
14
+ const [query, setQuery] = useState("");
15
+ const [results, setResults] = useState([]);
16
+ const [loading, setLoading] = useState(false);
17
+ const [creating, setCreating] = useState(false);
18
+ const [blockedUserIds, setBlockedUserIds] = useState([]);
19
+ const router = useRouter();
20
+ useEffect(() => {
21
+ if (open) {
22
+ fetchBlockerUsers();
23
+ }
24
+ }, [open]);
25
+ const fetchBlockerUsers = async () => {
26
+ const blocked = await userApi.getBlockerUsers();
27
+ setBlockedUserIds(blocked.map((u) => u._id));
28
+ };
29
+ async function handleSearch(e) {
30
+ e.preventDefault();
31
+ if (!query.trim())
32
+ return;
33
+ setLoading(true);
34
+ try {
35
+ const res = await userDetails(query, "user");
36
+ if (res.error) {
37
+ console.error("user not found:", res.error);
38
+ setResults([]);
39
+ }
40
+ else {
41
+ if (res.user && !blockedUserIds.includes(res.user.username)) {
42
+ setResults([res.user]);
43
+ }
44
+ else {
45
+ setResults([]);
46
+ }
47
+ }
48
+ }
49
+ catch (err) {
50
+ console.error(err);
51
+ }
52
+ finally {
53
+ setLoading(false);
54
+ }
55
+ }
56
+ async function startChat(userId) {
57
+ setOpen(false);
58
+ setQuery("");
59
+ setResults([]);
60
+ router.push(`/chat?u=${userId}`);
61
+ }
62
+ return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsxs("div", { className: "flex-1 flex items-center justify-center gap-2 py-2.5 bg-indigo-50 dark:bg-indigo-950/30 text-indigo-600 dark:text-indigo-400 rounded-xl text-sm font-medium hover:bg-indigo-100 dark:hover:bg-indigo-950/50 transition-colors cursor-pointer", children: [_jsx(MessageCircle, { className: "w-4 h-4" }), "New Chat"] }) }), _jsxs(DialogContent, { className: "sm:max-w-[425px]", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "New Chat" }) }), _jsxs("div", { className: "gap-4 py-4", children: [_jsxs("form", { onSubmit: handleSearch, className: "flex gap-2", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx(Search, { className: "absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" }), _jsx(Input, { placeholder: "Search by email, contact or username...", className: "pl-8", value: query, onChange: (e) => setQuery(e.target.value) })] }), _jsx(Button, { type: "submit", disabled: loading, children: loading ? _jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : "Search" })] }), _jsxs("div", { className: "mt-4 flex flex-col gap-2 max-h-[300px] overflow-y-auto", children: [results.length === 0 && !loading && query && (_jsx("p", { className: "text-center text-sm text-muted-foreground", children: "No users found." })), results.map((user) => (_jsxs("div", { className: "flex items-center justify-between p-2 hover:bg-accent rounded-md cursor-pointer", onClick: () => startChat(user.username || ""), children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs(Avatar, { children: [_jsx(AvatarImage, { src: user.profilePic }), _jsx(AvatarFallback, { children: _jsx(User, { className: "h-4 w-4" }) })] }), _jsxs("div", { children: [_jsxs("p", { className: "text-sm font-medium", children: [user.firstName, " ", user.lastName] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["@", user.username] })] })] }), creating ? _jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : null] }, user.username)))] })] })] })] }));
63
+ }
@@ -0,0 +1,2 @@
1
+ export declare function NewGroupDialog(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=NewGroupDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NewGroupDialog.d.ts","sourceRoot":"","sources":["../../../src/chat/dialogs/NewGroupDialog.tsx"],"names":[],"mappings":"AAcA,wBAAgB,cAAc,4CAwI7B"}
@@ -0,0 +1,69 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useMemo } from "react";
4
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "../../misc/dialog";
5
+ import { Button } from "../../misc/button";
6
+ import { Input } from "../../misc/input";
7
+ import { Users, Loader2, Camera } from "lucide-react";
8
+ import { chatApi } from "@myrjfa/state/dist/lib/actions/chat";
9
+ import { Avatar, AvatarFallback, AvatarImage } from "../../misc/avatar";
10
+ import { useRouter } from "next/navigation";
11
+ import { AddMemberDialog } from "./AddMemberDialog";
12
+ import { getCurrencies } from "@myrjfa/state/dist/lib/data/countryStates";
13
+ export function NewGroupDialog() {
14
+ const [open, setOpen] = useState(false);
15
+ const [groupName, setGroupName] = useState("");
16
+ const [selectedUsers, setSelectedUsers] = useState([]);
17
+ const [selectedCurrency, setSelectedCurrency] = useState("INR");
18
+ const [creating, setCreating] = useState(false);
19
+ const [previewImage, setPreviewImage] = useState("");
20
+ const [imageFile, setImageFile] = useState(null);
21
+ const router = useRouter();
22
+ // Get unique currencies and format them
23
+ const currencies = useMemo(() => {
24
+ const allCurrencies = getCurrencies();
25
+ return allCurrencies.map(currency => currency.toUpperCase()).sort();
26
+ }, []);
27
+ const handleImageChange = (e) => {
28
+ if (e.target.files && e.target.files[0]) {
29
+ const file = e.target.files[0];
30
+ setImageFile(file);
31
+ setPreviewImage(URL.createObjectURL(file));
32
+ }
33
+ };
34
+ async function createGroup() {
35
+ if (!groupName.trim() || selectedUsers.length === 0)
36
+ return;
37
+ setCreating(true);
38
+ try {
39
+ let imageUrl = null;
40
+ if (imageFile) {
41
+ const formData = new FormData();
42
+ formData.append("chatFiles", imageFile);
43
+ const res = await chatApi.uploadFiles(formData);
44
+ if (res.data && res.data.files && res.data.files.length > 0) {
45
+ imageUrl = res.data.files[0].location;
46
+ }
47
+ }
48
+ const conversation = await chatApi.createConversation({
49
+ type: "group",
50
+ name: groupName,
51
+ participantIds: selectedUsers.map(u => u.username || "").filter(Boolean),
52
+ image: imageUrl,
53
+ expenseCurrency: selectedCurrency
54
+ });
55
+ setOpen(false);
56
+ setGroupName("");
57
+ setSelectedUsers([]);
58
+ setSelectedCurrency("INR");
59
+ setPreviewImage("");
60
+ setImageFile(null);
61
+ router.replace(`/chat?id=${conversation._id}`);
62
+ }
63
+ catch (err) {
64
+ console.error("Failed to create group:", err);
65
+ setCreating(false);
66
+ }
67
+ }
68
+ return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { asChild: true, children: _jsxs("div", { className: "flex-1 flex items-center justify-center gap-2 py-2.5 bg-purple-50 dark:bg-purple-950/30 text-purple-600 dark:text-purple-400 rounded-xl text-sm font-medium hover:bg-purple-100 dark:hover:bg-purple-950/50 transition-colors cursor-pointer", children: [_jsx(Users, { className: "w-4 h-4" }), "New Group"] }) }), _jsxs(DialogContent, { className: "sm:max-w-[500px] mt-15", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: "New Group Chat" }) }), _jsxs("div", { className: "flex flex-col gap-4 py-4", children: [_jsxs("div", { className: "flex flex-col items-center gap-2", children: [_jsxs("div", { className: "relative group cursor-pointer", children: [_jsxs(Avatar, { className: "w-24 h-24 shadow-md", children: [_jsx(AvatarImage, { src: previewImage }), _jsx(AvatarFallback, { className: "bg-gradient-to-br from-indigo-500 to-purple-600 text-white text-2xl", children: _jsx(Users, { className: "w-10 h-10" }) })] }), _jsxs("label", { className: "absolute inset-0 flex items-center justify-center bg-black/40 text-white opacity-0 group-hover:opacity-100 rounded-full transition-opacity cursor-pointer", children: [_jsx(Camera, { className: "w-8 h-8" }), _jsx("input", { type: "file", className: "hidden", accept: "image/*", onChange: handleImageChange })] })] }), _jsx("p", { className: "text-xs text-gray-500", children: "Click to change group icon" })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: "Group Name" }), _jsx(Input, { placeholder: "Group Name", value: groupName, onChange: (e) => setGroupName(e.target.value) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium", children: "Expense Currency" }), _jsxs("select", { value: selectedCurrency, onChange: (e) => setSelectedCurrency(e.target.value), className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white outline-none focus:border-purple-500 focus:ring-1 focus:ring-purple-500 transition-colors", children: [_jsx("option", { value: "", children: "Select Currency" }), currencies.map((currency) => (_jsx("option", { value: currency, children: currency }, currency)))] }), _jsx("p", { className: "text-xs text-gray-500", children: "This currency will be used for all expense tracking in this group" })] }), _jsx(AddMemberDialog, { selectedUsers: selectedUsers, setSelectedUsers: setSelectedUsers, excludeUsernames: selectedUsers.map(u => u.username || "") })] }), _jsx("div", { className: "flex justify-end pt-4", children: _jsxs(Button, { onClick: createGroup, variant: "default", disabled: creating || !groupName || selectedUsers.length === 0 || !selectedCurrency, children: [creating ? _jsx(Loader2, { className: "h-4 w-4 animate-spin mr-2" }) : null, "Create Group"] }) })] })] })] }));
69
+ }
@@ -0,0 +1,11 @@
1
+ import type { Conversation } from "@myrjfa/state/dist/lib/models/chat";
2
+ interface ParticipantsDialogProps {
3
+ open: boolean;
4
+ onOpenChange: (open: boolean) => void;
5
+ conversation: Conversation;
6
+ currentUserId: string;
7
+ onUpdate: () => void;
8
+ }
9
+ export declare function ParticipantsDialog({ open, onOpenChange, conversation, currentUserId, onUpdate }: ParticipantsDialogProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
11
+ //# sourceMappingURL=ParticipantsDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParticipantsDialog.d.ts","sourceRoot":"","sources":["../../../src/chat/dialogs/ParticipantsDialog.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,oCAAoC,CAAC;AAQpF,UAAU,uBAAuB;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,QAAQ,EACX,EAAE,uBAAuB,2CA4KzB"}