@myrjfa/ui 1.1.0 → 1.1.1

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 (131) hide show
  1. package/dist/chat/ChatMain.d.ts.map +1 -1
  2. package/dist/chat/ChatMain.js +173 -145
  3. package/dist/chat/ChatSidebar.js +7 -8
  4. package/dist/chat/ConversationList.d.ts.map +1 -1
  5. package/dist/chat/ConversationList.js +20 -18
  6. package/dist/chat/ExpensePanel.d.ts +4 -1
  7. package/dist/chat/ExpensePanel.d.ts.map +1 -1
  8. package/dist/chat/ExpensePanel.js +215 -195
  9. package/dist/chat/MessageBubble.d.ts +5 -3
  10. package/dist/chat/MessageBubble.d.ts.map +1 -1
  11. package/dist/chat/MessageBubble.js +185 -103
  12. package/dist/chat/MessageInput.d.ts +8 -1
  13. package/dist/chat/MessageInput.d.ts.map +1 -1
  14. package/dist/chat/MessageInput.js +182 -165
  15. package/dist/chat/MilestonePanel.d.ts +4 -1
  16. package/dist/chat/MilestonePanel.d.ts.map +1 -1
  17. package/dist/chat/MilestonePanel.js +150 -150
  18. package/dist/chat/components/ParticipantSelector.d.ts +11 -0
  19. package/dist/chat/components/ParticipantSelector.d.ts.map +1 -0
  20. package/dist/chat/components/ParticipantSelector.js +17 -0
  21. package/dist/chat/components/SplitInputs.d.ts +11 -0
  22. package/dist/chat/components/SplitInputs.d.ts.map +1 -0
  23. package/dist/chat/components/SplitInputs.js +83 -0
  24. package/dist/chat/components/SplitTypeSelector.d.ts +8 -0
  25. package/dist/chat/components/SplitTypeSelector.d.ts.map +1 -0
  26. package/dist/chat/components/SplitTypeSelector.js +13 -0
  27. package/dist/chat/dialogs/AddMemberDialog.js +30 -90
  28. package/dist/chat/dialogs/CameraDialog.js +38 -86
  29. package/dist/chat/dialogs/CreatePollDialog.d.ts +8 -0
  30. package/dist/chat/dialogs/CreatePollDialog.d.ts.map +1 -0
  31. package/dist/chat/dialogs/CreatePollDialog.js +38 -0
  32. package/dist/chat/dialogs/ExpenseAnalyticsDialog.d.ts +19 -0
  33. package/dist/chat/dialogs/ExpenseAnalyticsDialog.d.ts.map +1 -0
  34. package/dist/chat/dialogs/ExpenseAnalyticsDialog.js +52 -0
  35. package/dist/chat/dialogs/GroupInfoDialog.d.ts.map +1 -1
  36. package/dist/chat/dialogs/GroupInfoDialog.js +55 -92
  37. package/dist/chat/dialogs/GroupInfoViewDialog.js +5 -6
  38. package/dist/chat/dialogs/NewChatDialog.d.ts.map +1 -1
  39. package/dist/chat/dialogs/NewChatDialog.js +49 -113
  40. package/dist/chat/dialogs/NewGroupDialog.d.ts.map +1 -1
  41. package/dist/chat/dialogs/NewGroupDialog.js +49 -92
  42. package/dist/chat/dialogs/ParticipantsDialog.d.ts.map +1 -1
  43. package/dist/chat/dialogs/ParticipantsDialog.js +68 -118
  44. package/dist/chat/dialogs/UserProfileDialog.d.ts +10 -0
  45. package/dist/chat/dialogs/UserProfileDialog.d.ts.map +1 -0
  46. package/dist/chat/dialogs/UserProfileDialog.js +100 -0
  47. package/dist/dashboard/action-buttons.js +4 -5
  48. package/dist/dashboard/app-sidebar.js +46 -128
  49. package/dist/dashboard/card-header.js +1 -2
  50. package/dist/dashboard/dashboard-components.js +3 -5
  51. package/dist/dashboard/data-table.js +21 -33
  52. package/dist/dashboard/nav-main.js +4 -5
  53. package/dist/dashboard/nav-secondary.js +2 -25
  54. package/dist/dashboard/stats-card.js +1 -2
  55. package/dist/dashboard/table.js +8 -54
  56. package/dist/header/mobile-nav-item.js +1 -2
  57. package/dist/header/mobile-navbar.js +8 -9
  58. package/dist/header/nav-item.js +1 -2
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +1 -1
  62. package/dist/main/footer.js +5 -9
  63. package/dist/main/navbar.js +53 -136
  64. package/dist/main/paginationBar.js +12 -14
  65. package/dist/main/portfolio/portfolioItem.js +1 -2
  66. package/dist/main/portfolio/portfolioPopup.js +16 -17
  67. package/dist/main/review-page.d.ts.map +1 -1
  68. package/dist/main/review-page.js +131 -198
  69. package/dist/misc/accordion.js +8 -34
  70. package/dist/misc/alert-dialog.js +22 -55
  71. package/dist/misc/alert.js +7 -32
  72. package/dist/misc/alertdialogclaude.js +50 -125
  73. package/dist/misc/avatar.js +6 -31
  74. package/dist/misc/badge.js +4 -27
  75. package/dist/misc/button.js +4 -27
  76. package/dist/misc/card.js +12 -40
  77. package/dist/misc/carousel.js +37 -61
  78. package/dist/misc/checkbox.js +2 -25
  79. package/dist/misc/command.js +18 -49
  80. package/dist/misc/datepicker.js +66 -76
  81. package/dist/misc/dialog.js +20 -52
  82. package/dist/misc/dropdown-menu.js +30 -67
  83. package/dist/misc/faq.js +2 -3
  84. package/dist/misc/file-preview.js +43 -97
  85. package/dist/misc/filter.d.ts.map +1 -1
  86. package/dist/misc/filter.js +18 -21
  87. package/dist/misc/filterBar.js +136 -160
  88. package/dist/misc/form.js +39 -60
  89. package/dist/misc/image-cropper.d.ts +10 -0
  90. package/dist/misc/image-cropper.d.ts.map +1 -0
  91. package/dist/misc/image-cropper.js +44 -0
  92. package/dist/misc/input.js +2 -25
  93. package/dist/misc/label.js +2 -25
  94. package/dist/misc/line-carousel.js +3 -13
  95. package/dist/misc/multiImageUploader.js +2 -3
  96. package/dist/misc/newsLetter.js +18 -78
  97. package/dist/misc/notification.d.ts +1 -2
  98. package/dist/misc/notification.d.ts.map +1 -1
  99. package/dist/misc/notification.js +89 -125
  100. package/dist/misc/pagination.js +16 -45
  101. package/dist/misc/password-eye.js +2 -3
  102. package/dist/misc/popover.d.ts.map +1 -1
  103. package/dist/misc/popover.js +8 -34
  104. package/dist/misc/progress.js +2 -25
  105. package/dist/misc/scroll-area.js +5 -29
  106. package/dist/misc/searchBar.js +27 -31
  107. package/dist/misc/searchBox.js +18 -22
  108. package/dist/misc/select.d.ts.map +1 -1
  109. package/dist/misc/select.js +22 -54
  110. package/dist/misc/separator.js +2 -25
  111. package/dist/misc/share.js +48 -104
  112. package/dist/misc/sheet.js +21 -53
  113. package/dist/misc/sidebar.js +112 -153
  114. package/dist/misc/skeleton.js +2 -25
  115. package/dist/misc/slider.js +3 -26
  116. package/dist/misc/starRating.js +5 -15
  117. package/dist/misc/stateCard.js +7 -8
  118. package/dist/misc/switch.d.ts +5 -0
  119. package/dist/misc/switch.d.ts.map +1 -0
  120. package/dist/misc/switch.js +8 -0
  121. package/dist/misc/tabs.js +8 -34
  122. package/dist/misc/textarea.js +2 -25
  123. package/dist/misc/tile.js +31 -92
  124. package/dist/misc/tooltip.js +8 -34
  125. package/dist/profile/socialLink.d.ts +1 -1
  126. package/dist/profile/socialLink.d.ts.map +1 -1
  127. package/dist/profile/socialLink.js +53 -69
  128. package/dist/utils/permissions.d.ts +7 -0
  129. package/dist/utils/permissions.d.ts.map +1 -0
  130. package/dist/utils/permissions.js +38 -0
  131. package/package.json +3 -2
@@ -1,215 +1,235 @@
1
1
  "use client";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __generator = (this && this.__generator) || function (thisArg, body) {
12
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
- function verb(n) { return function (v) { return step([n, v]); }; }
15
- function step(op) {
16
- if (f) throw new TypeError("Generator is already executing.");
17
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
- if (y = 0, t) op = [op[0] & 2, t.value];
20
- switch (op[0]) {
21
- case 0: case 1: t = op; break;
22
- case 4: _.label++; return { value: op[1], done: false };
23
- case 5: _.label++; y = op[1]; op = [0]; continue;
24
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
- default:
26
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
- if (t[2]) _.ops.pop();
31
- _.trys.pop(); continue;
32
- }
33
- op = body.call(thisArg, _);
34
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
- }
37
- };
38
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
39
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
40
- if (ar || !(i in from)) {
41
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
42
- ar[i] = from[i];
43
- }
44
- }
45
- return to.concat(ar || Array.prototype.slice.call(from));
46
- };
47
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
48
3
  import { useState, useEffect } from "react";
49
4
  import { chatApi } from "@myrjfa/state/dist/lib/actions/chat";
50
- import { Receipt, Plus, Trash2, MoreVertical, Users, MapPin, Image as ImageIcon, Loader2, Navigation } from "lucide-react";
5
+ import { Receipt, Plus, Trash2, Pencil, CreditCard, Users, MapPin, Image as ImageIcon, Loader2, Navigation, Lock, Unlock, ReceiptText, BarChart3, CheckCircle, X, ChevronDown, PiggyBankIcon } from "lucide-react";
51
6
  import { Button } from "../misc/button";
52
- import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../misc/dropdown-menu";
53
- export function ExpensePanel(_a) {
54
- var _this = this;
55
- var conversationId = _a.conversationId, currentUserId = _a.currentUserId, _b = _a.isFullScreen, isFullScreen = _b === void 0 ? false : _b;
56
- var _c = useState([]), expenses = _c[0], setExpenses = _c[1];
57
- var _d = useState(null), balances = _d[0], setBalances = _d[1];
58
- var _e = useState(true), loading = _e[0], setLoading = _e[1];
59
- var _f = useState(false), isAdding = _f[0], setIsAdding = _f[1];
60
- var _g = useState(""), newTitle = _g[0], setNewTitle = _g[1];
61
- var _h = useState(""), newAmount = _h[0], setNewAmount = _h[1];
62
- var _j = useState(null), newLocation = _j[0], setNewLocation = _j[1];
63
- var _k = useState(null), receiptFile = _k[0], setReceiptFile = _k[1];
64
- var _l = useState(false), uploading = _l[0], setUploading = _l[1];
65
- var _m = useState(false), gettingLocation = _m[0], setGettingLocation = _m[1];
66
- var fetchData = function () { return __awaiter(_this, void 0, void 0, function () {
67
- var _a, expData, balData;
68
- return __generator(this, function (_b) {
69
- switch (_b.label) {
70
- case 0:
71
- setLoading(true);
72
- return [4 /*yield*/, Promise.all([
73
- chatApi.getExpenses(conversationId),
74
- chatApi.getBalances(conversationId)
75
- ])];
76
- case 1:
77
- _a = _b.sent(), expData = _a[0], balData = _a[1];
78
- setExpenses(expData);
79
- setBalances(balData);
80
- setLoading(false);
81
- return [2 /*return*/];
82
- }
83
- });
84
- }); };
85
- useEffect(function () {
7
+ import { Input } from "../misc/input";
8
+ import { Textarea } from "../misc/textarea";
9
+ import { confirmActions, deleteActions, showAlert } from "../misc/alertdialogclaude";
10
+ import { ExpenseAnalyticsDialog } from "./dialogs/ExpenseAnalyticsDialog";
11
+ import { SplitTypeSelector } from "./components/SplitTypeSelector";
12
+ import { SplitInputs } from "./components/SplitInputs";
13
+ import { ParticipantSelector } from "./components/ParticipantSelector";
14
+ import { Label } from "../misc/label";
15
+ export function ExpensePanel({ conversationId, currentUserId, isFullScreen = false, isFeatureEnabled = false, isAdmin = false, onEnableFeature }) {
16
+ const [expenses, setExpenses] = useState([]);
17
+ const [balances, setBalances] = useState(null);
18
+ const [loading, setLoading] = useState(true);
19
+ const [isAdding, setIsAdding] = useState(false);
20
+ const [showSettleMode, setShowSettleMode] = useState(false);
21
+ const [newTitle, setNewTitle] = useState("");
22
+ const [newAmount, setNewAmount] = useState("");
23
+ const [newCategory, setNewCategory] = useState("");
24
+ const [newDescription, setNewDescription] = useState("");
25
+ const [newLocation, setNewLocation] = useState(null);
26
+ const [receiptFile, setReceiptFile] = useState(null);
27
+ const [uploading, setUploading] = useState(false);
28
+ const [gettingLocation, setGettingLocation] = useState(false);
29
+ const [categories, setCategories] = useState([]);
30
+ const [showAnalytics, setShowAnalytics] = useState(false);
31
+ const [settling, setSettling] = useState(null);
32
+ const [expenseId, setExpenseId] = useState(null);
33
+ const [groupParticipants, setGroupParticipants] = useState([]);
34
+ const [showMoreOptions, setShowMoreOptions] = useState(false);
35
+ const [splitType, setSplitType] = useState("equal");
36
+ const [splitParticipants, setSplitParticipants] = useState([]);
37
+ const [splits, setSplits] = useState([]);
38
+ const [expandedExpenseId, setExpandedExpenseId] = useState(null);
39
+ const [showSplitModal, setShowSplitModal] = useState(false);
40
+ const [selectedExpenseForSplit, setSelectedExpenseForSplit] = useState(null);
41
+ const fetchData = async () => {
42
+ var _a;
43
+ setLoading(true);
44
+ const [expData, balData, convData] = await Promise.all([
45
+ chatApi.getExpenses(conversationId),
46
+ chatApi.getBalances(conversationId),
47
+ chatApi.getConversation(conversationId)
48
+ ]);
49
+ setExpenses(expData);
50
+ setBalances(balData);
51
+ const participants = ((_a = convData === null || convData === void 0 ? void 0 : convData.participants) === null || _a === void 0 ? void 0 : _a.map((p) => p.userId)) || [];
52
+ setGroupParticipants(participants);
53
+ setLoading(false);
54
+ };
55
+ useEffect(() => {
86
56
  fetchData();
57
+ fetchCategories();
87
58
  }, [conversationId]);
88
- var handleGetLocation = function () {
59
+ const fetchCategories = async () => {
60
+ try {
61
+ const cats = await chatApi.getExpenseCategories(conversationId);
62
+ setCategories(cats || []);
63
+ }
64
+ catch (error) {
65
+ console.error("Failed to fetch categories:", error);
66
+ }
67
+ };
68
+ const userOwesAmount = () => {
69
+ if (!balances)
70
+ return 0;
71
+ const userBalance = balances.balances.find(b => b.userId === currentUserId);
72
+ return userBalance && userBalance.netBalance < -0.01 ? Math.abs(userBalance.netBalance) : 0;
73
+ };
74
+ const getUserSettlements = () => {
75
+ if (!(balances === null || balances === void 0 ? void 0 : balances.settlements))
76
+ return { owes: [], isOwed: [] };
77
+ const owes = balances.settlements.filter(s => s.from === currentUserId);
78
+ const isOwed = balances.settlements.filter(s => s.to === currentUserId);
79
+ return { owes, isOwed };
80
+ };
81
+ const handleGetLocation = () => {
89
82
  if (!navigator.geolocation) {
90
83
  alert("Geolocation not supported by your browser");
91
84
  return;
92
85
  }
93
86
  setGettingLocation(true);
94
- navigator.geolocation.getCurrentPosition(function (position) { return __awaiter(_this, void 0, void 0, function () {
95
- var _a, latitude, longitude, response, data, address, err_1;
96
- var _b, _c, _d;
97
- return __generator(this, function (_e) {
98
- switch (_e.label) {
99
- case 0:
100
- _a = position.coords, latitude = _a.latitude, longitude = _a.longitude;
101
- _e.label = 1;
102
- case 1:
103
- _e.trys.push([1, 4, , 5]);
104
- return [4 /*yield*/, fetch("https://nominatim.openstreetmap.org/reverse?format=json&lat=".concat(latitude, "&lon=").concat(longitude), { headers: { 'Accept-Language': 'en' } })];
105
- case 2:
106
- response = _e.sent();
107
- return [4 /*yield*/, response.json()];
108
- case 3:
109
- data = _e.sent();
110
- address = ((_b = data.address) === null || _b === void 0 ? void 0 : _b.city) || ((_c = data.address) === null || _c === void 0 ? void 0 : _c.town) || ((_d = data.address) === null || _d === void 0 ? void 0 : _d.village) || "".concat(latitude.toFixed(4), ", ").concat(longitude.toFixed(4));
111
- setNewLocation(address);
112
- return [3 /*break*/, 5];
113
- case 4:
114
- err_1 = _e.sent();
115
- // Fallback to coordinates if reverse geocoding fails
116
- setNewLocation("".concat(latitude.toFixed(4), ", ").concat(longitude.toFixed(4)));
117
- return [3 /*break*/, 5];
118
- case 5:
119
- setGettingLocation(false);
120
- return [2 /*return*/];
121
- }
122
- });
123
- }); }, function (error) {
124
- console.error("Geolocation error:", error);
125
- alert("Failed to get location. Please check permissions.");
87
+ navigator.geolocation.getCurrentPosition(async (position) => {
88
+ const { latitude, longitude } = position.coords;
89
+ const mapLink = `https://www.google.com/maps?q=${latitude},${longitude}`;
90
+ setNewLocation(mapLink);
91
+ setGettingLocation(false);
92
+ }, (error) => {
93
+ console.error("Error getting location:", error);
94
+ showAlert("Could not get location.");
126
95
  setGettingLocation(false);
127
96
  });
128
97
  };
129
- var handleCreate = function () { return __awaiter(_this, void 0, void 0, function () {
130
- var receiptUrl, formData, res, amount, expense_1, balData, err_2;
131
- return __generator(this, function (_a) {
132
- switch (_a.label) {
133
- case 0:
134
- if (!newTitle.trim() || !newAmount)
135
- return [2 /*return*/];
136
- setUploading(true);
137
- _a.label = 1;
138
- case 1:
139
- _a.trys.push([1, 7, 8, 9]);
140
- receiptUrl = undefined;
141
- if (!receiptFile) return [3 /*break*/, 3];
142
- formData = new FormData();
143
- formData.append("chatFiles", receiptFile);
144
- return [4 /*yield*/, chatApi.uploadFiles(formData)];
145
- case 2:
146
- res = _a.sent();
147
- if (res.data && res.data.files && res.data.files.length > 0) {
148
- receiptUrl = res.data.files[0].location;
149
- }
150
- _a.label = 3;
151
- case 3:
152
- amount = parseFloat(newAmount);
153
- return [4 /*yield*/, chatApi.createExpense(conversationId, {
154
- title: newTitle.trim(),
155
- amount: amount,
156
- paidBy: [{ userId: currentUserId, amount: amount }],
157
- location: newLocation || undefined,
158
- receipt: receiptUrl
159
- })];
160
- case 4:
161
- expense_1 = _a.sent();
162
- if (!expense_1) return [3 /*break*/, 6];
163
- setExpenses(function (prev) { return __spreadArray([expense_1], prev, true); });
164
- setNewTitle("");
165
- setNewAmount("");
166
- setNewLocation(null);
167
- setReceiptFile(null);
168
- setIsAdding(false);
169
- return [4 /*yield*/, chatApi.getBalances(conversationId)];
170
- case 5:
171
- balData = _a.sent();
172
- setBalances(balData);
173
- _a.label = 6;
174
- case 6: return [3 /*break*/, 9];
175
- case 7:
176
- err_2 = _a.sent();
177
- console.error("Failed to create expense:", err_2);
178
- return [3 /*break*/, 9];
179
- case 8:
180
- setUploading(false);
181
- return [7 /*endfinally*/];
182
- case 9: return [2 /*return*/];
98
+ const resetForm = () => {
99
+ setNewTitle("");
100
+ setNewAmount("");
101
+ setNewCategory("");
102
+ setNewDescription("");
103
+ setNewLocation(null);
104
+ setReceiptFile(null);
105
+ setShowMoreOptions(false);
106
+ setIsAdding(false);
107
+ setExpenseId(null);
108
+ setSplitType("equal");
109
+ setSplitParticipants([]);
110
+ setSplits([]);
111
+ };
112
+ const handleCreate = async () => {
113
+ var _a, _b;
114
+ if (!newTitle.trim() || !newAmount)
115
+ return;
116
+ const amount = parseFloat(newAmount);
117
+ if (amount <= 0) {
118
+ showAlert("Amount must be greater than 0");
119
+ return;
120
+ }
121
+ setUploading(true);
122
+ try {
123
+ let receiptUrl = undefined;
124
+ if (receiptFile) {
125
+ const formData = new FormData();
126
+ formData.append("chatFiles", receiptFile);
127
+ const res = await chatApi.uploadFiles(formData);
128
+ if ((_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]) {
129
+ receiptUrl = res.data.files[0].location;
130
+ }
183
131
  }
184
- });
185
- }); };
186
- var handleDelete = function (id) { return __awaiter(_this, void 0, void 0, function () {
187
- var balData;
188
- return __generator(this, function (_a) {
189
- switch (_a.label) {
190
- case 0:
191
- if (!window.confirm("Are you sure you want to delete this expense?")) return [3 /*break*/, 3];
192
- return [4 /*yield*/, chatApi.deleteExpense(id)];
193
- case 1:
194
- _a.sent();
195
- setExpenses(function (prev) { return prev.filter(function (e) { return e._id !== id; }); });
196
- return [4 /*yield*/, chatApi.getBalances(conversationId)];
197
- case 2:
198
- balData = _a.sent();
199
- setBalances(balData);
200
- _a.label = 3;
201
- case 3: return [2 /*return*/];
132
+ const expenseData = {
133
+ title: newTitle.trim(),
134
+ amount,
135
+ category: newCategory.trim() || "General",
136
+ description: newDescription.trim() || undefined,
137
+ paidBy: [{ userId: currentUserId, amount }],
138
+ splitType,
139
+ splits: splitType != "equal" && splits,
140
+ location: newLocation || undefined,
141
+ receipt: receiptUrl
142
+ };
143
+ const expenseResponse = expenseId
144
+ ? await chatApi.updateExpense(expenseId, expenseData)
145
+ : await chatApi.createExpense(conversationId, expenseData);
146
+ if (expenseResponse.error) {
147
+ console.error("Failed to create expense:", expenseResponse.error);
148
+ await showAlert(expenseResponse.error, "Failed to create expense");
149
+ return;
202
150
  }
203
- });
204
- }); };
205
- var panelClasses = isFullScreen
151
+ else {
152
+ if (expenseResponse.data) {
153
+ await fetchData();
154
+ resetForm();
155
+ }
156
+ }
157
+ }
158
+ catch (err) {
159
+ console.error("Failed to create expense:", err);
160
+ showAlert(err, "Failed to create expense");
161
+ }
162
+ finally {
163
+ setUploading(false);
164
+ }
165
+ };
166
+ const handleDelete = async (id) => {
167
+ let confirmDelete = true;
168
+ await showAlert("Are you sure you want to delete this expense?", undefined, deleteActions(() => confirmDelete = false));
169
+ if (confirmDelete) {
170
+ const response = await chatApi.deleteExpense(id);
171
+ if (response.error) {
172
+ console.log("Unable to delete expense:" + id + "Error: " + response.error);
173
+ await showAlert("Unable to delete expense");
174
+ return;
175
+ }
176
+ setExpenses(prev => prev.filter(e => e._id !== id));
177
+ const balData = await chatApi.getBalances(conversationId);
178
+ setBalances(balData);
179
+ }
180
+ };
181
+ const handleEdit = async (expense) => {
182
+ setExpenseId(expense._id);
183
+ setNewTitle(expense.title);
184
+ setNewAmount(expense.amount.toString());
185
+ setNewCategory(expense.category || "");
186
+ setNewDescription(expense.description || "");
187
+ setNewLocation(expense.location || null);
188
+ setSplitType(expense.splitType);
189
+ setSplitParticipants(expense.splits.map(s => s.userId));
190
+ setSplits(expense.splits);
191
+ setIsAdding(true);
192
+ };
193
+ const handleSettleSplit = async (payment) => {
194
+ setSettling(payment.from + payment.to);
195
+ let confirmPayment = true;
196
+ await showAlert(`Pay ₹${payment.amount.toFixed(0)} to ${payment.to}`, undefined, confirmActions(() => confirmPayment = false));
197
+ if (confirmPayment) {
198
+ const response = await chatApi.settleSplit(conversationId, payment.to, payment.amount);
199
+ if (response.error) {
200
+ console.log("Error recording payment: " + response.error);
201
+ showAlert("Error recording payment");
202
+ }
203
+ else {
204
+ setExpenses(prev => [response.data, ...prev]);
205
+ }
206
+ }
207
+ setSettling(null);
208
+ };
209
+ const panelClasses = isFullScreen
206
210
  ? "w-full h-full flex flex-col"
207
211
  : "flex flex-col h-full w-full md:w-80 border-l border-gray-200 dark:border-gray-700";
208
- return (_jsxs("div", { className: "bg-white dark:bg-gray-900 ".concat(panelClasses), children: [_jsx("div", { className: "p-4 border-b border-gray-200 dark:border-gray-700 flex-shrink-0", children: _jsx("h3", { className: "font-semibold text-gray-900 dark:text-white", children: "Expenses" }) }), balances && (_jsxs("div", { className: "p-3 bg-teal-50 dark:bg-teal-900/20 mx-3 mt-3 rounded-xl border border-teal-100 dark:border-teal-800 flex-shrink-0", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Users, { className: "w-4 h-4 text-teal-500" }), _jsx("span", { className: "text-xs font-semibold text-teal-700 dark:text-teal-300 uppercase tracking-wider", children: "Group Balance" })] }), balances.balances.map(function (b) { return (_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 ".concat(b.netBalance >= 0 ? "text-green-600" : "text-red-500"), children: [b.netBalance >= 0 ? "+" : "", "\u20B9", b.netBalance.toFixed(2)] })] }, b.userId)); })] })), _jsx("div", { className: "flex-1 overflow-y-auto p-4 space-y-4 pt-0", children: loading ? (_jsx("div", { className: "flex justify-center py-8", children: _jsx("div", { className: "animate-spin w-5 h-5 border-2 border-teal-500 border-t-transparent rounded-full" }) })) : expenses.length === 0 && !isAdding ? (_jsx("div", { className: "text-center py-8 text-gray-400 text-sm", children: "No expenses yet" })) : (_jsxs(_Fragment, { children: [isAdding && (_jsxs("div", { className: "bg-teal-50 dark:bg-teal-900/20 p-3 rounded-xl border border-teal-100 dark:border-teal-800", children: [_jsx("input", { autoFocus: true, value: newTitle, onChange: function (e) { return setNewTitle(e.target.value); }, placeholder: "What for?", className: "w-full bg-white dark:bg-gray-800 border-none outline-none rounded-lg p-2 text-sm text-gray-900 dark:text-white mb-2 shadow-sm" }), _jsxs("div", { className: "relative mb-2", children: [_jsx("span", { className: "absolute left-2 top-2 text-gray-400 text-sm", children: "\u20B9" }), _jsx("input", { type: "number", value: newAmount, onChange: function (e) { return setNewAmount(e.target.value); }, placeholder: "0.00", className: "w-full bg-white dark:bg-gray-800 border-none outline-none rounded-lg p-2 pl-6 text-sm text-gray-900 dark:text-white shadow-sm" })] }), _jsxs("div", { className: "mb-2 flex gap-2", children: [_jsx("button", { onClick: handleGetLocation, disabled: gettingLocation || uploading, className: "flex-1 flex items-center justify-center gap-2 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed border border-gray-200 dark:border-gray-700 rounded-lg p-2 text-sm text-gray-600 dark:text-gray-300 transition-colors", children: gettingLocation ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "w-4 h-4 animate-spin" }), _jsx("span", { children: "Getting..." })] })) : (_jsxs(_Fragment, { children: [_jsx(Navigation, { className: "w-4 h-4" }), _jsx("span", { children: "Get Location" })] })) }), newLocation && (_jsx("button", { onClick: function () { return setNewLocation(null); }, className: "px-2 py-2 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20 text-red-500 transition-colors", title: "Clear location", children: "\u2715" }))] }), newLocation && (_jsxs("div", { className: "mb-2 p-2 bg-white dark:bg-gray-800 rounded-lg border border-teal-200 dark:border-teal-800 flex items-center gap-2", children: [_jsx(MapPin, { className: "w-4 h-4 text-teal-500 flex-shrink-0" }), _jsx("span", { className: "text-xs text-gray-700 dark:text-gray-300 truncate", children: newLocation })] })), _jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsxs("label", { className: "flex-1 flex items-center gap-2 text-xs text-gray-500 cursor-pointer hover:text-teal-500 transition-colors bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-2", children: [_jsx(ImageIcon, { className: "w-4 h-4" }), receiptFile ? _jsx("span", { className: "text-teal-500 truncate max-w-[150px] text-xs", children: receiptFile.name }) : "Add Receipt", _jsx("input", { type: "file", className: "hidden", accept: "image/*", onChange: function (e) { var _a; return setReceiptFile(((_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0]) || null); } })] }), receiptFile && _jsx("button", { onClick: function () { return setReceiptFile(null); }, className: "text-red-400 hover:text-red-500 p-2", children: _jsx(Trash2, { className: "w-3 h-3" }) })] }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: function () {
209
- setIsAdding(false);
210
- setNewLocation(null);
211
- }, disabled: uploading || gettingLocation, children: "Cancel" }), _jsxs(Button, { size: "sm", onClick: handleCreate, disabled: uploading || gettingLocation, className: "bg-gradient-to-r from-teal-500 to-cyan-600 hover:from-teal-600 hover:to-cyan-700 text-white", children: [uploading && _jsx(Loader2, { className: "w-3 h-3 mr-1 animate-spin" }), "Add"] })] })] })), expenses.map(function (e) {
212
- var _a;
213
- return (_jsxs("div", { className: "group flex items-start gap-3 p-3 bg-gray-50 dark:bg-gray-800/30 border border-gray-100 dark:border-gray-800 rounded-xl transition-colors", children: [_jsx("div", { className: "mt-1 p-2 bg-teal-100 dark:bg-teal-900/30 rounded-lg text-teal-500 flex-shrink-0", children: _jsx(Receipt, { className: "w-4 h-4" }) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex justify-between items-start", children: [_jsx("p", { className: "text-sm font-semibold text-gray-800 dark:text-gray-200 truncate", children: e.title }), _jsxs("p", { className: "text-sm font-bold text-gray-900 dark:text-white flex-shrink-0 ml-2", children: ["\u20B9", e.amount] })] }), _jsxs("div", { className: "flex justify-between items-start mt-1 flex-wrap gap-1", children: [_jsxs("p", { className: "text-[10px] text-gray-400", children: ["by ", (_a = e.paidBy[0]) === null || _a === void 0 ? void 0 : _a.userId, " \u2022 ", new Date(e.date).toLocaleDateString()] }), (e.location || e.receipt) && (_jsxs("div", { className: "flex items-center gap-2 mt-1 flex-wrap", children: [e.location && (_jsxs("a", { href: e.location.startsWith('http') ? e.location : "https://maps.google.com/?q=".concat(e.location), target: "_blank", rel: "noopener noreferrer", className: "flex items-center text-[10px] text-blue-500 hover:underline", children: [_jsx(MapPin, { className: "w-3 h-3 mr-0.5" }), e.location.length > 20 ? e.location.substring(0, 20) + "..." : e.location] })), e.receipt && (_jsxs("a", { href: e.receipt, target: "_blank", rel: "noopener noreferrer", className: "flex items-center text-[10px] text-blue-500 hover:underline", children: [_jsx(Receipt, { className: "w-3 h-3 mr-0.5" }), "View"] }))] }))] })] }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: "opacity-0 group-hover:opacity-100 p-1 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-md transition-all mt-2 flex-shrink-0", children: _jsx(MoreVertical, { className: "w-3 h-3 text-gray-400" }) }) }), _jsx(DropdownMenuContent, { align: "end", children: _jsxs(DropdownMenuItem, { className: "text-red-500", onClick: function () { return handleDelete(e._id); }, children: [_jsx(Trash2, { className: "w-4 h-4 mr-2" }), "Delete"] }) })] })] }, e._id));
214
- })] })) }), !isAdding && (_jsx("div", { className: "p-4 border-t border-gray-200 dark:border-gray-700 flex-shrink-0", children: _jsxs(Button, { onClick: function () { return setIsAdding(true); }, className: "w-full bg-gradient-to-r from-teal-500 to-cyan-600 hover:from-teal-600 hover:to-cyan-700 text-white rounded-lg flex items-center justify-center gap-2", children: [_jsx(Plus, { className: "w-4 h-4" }), "Add Expense"] }) }))] }));
212
+ return (_jsxs("div", { className: `bg-white dark:bg-gray-900 ${panelClasses}`, children: [_jsxs("div", { className: "p-4 border-b border-gray-200 dark:border-gray-700 flex-shrink-0 flex items-center justify-between", children: [_jsx("h3", { className: "font-semibold text-gray-900 dark:text-white", children: "Expenses" }), expenses.length > 0 && (_jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setShowAnalytics(prev => !prev), className: "text-teal-600 hover:text-teal-700 dark:text-teal-400", children: [_jsx(BarChart3, { className: "w-4 h-4 mr-1" }), "Analytics"] }))] }), !isAdding && balances && (_jsxs("div", { className: "p-3 bg-teal-50 dark:bg-teal-900/20 mx-3 mt-3 rounded-xl border border-teal-100 dark:border-teal-800 flex-shrink-0", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Users, { className: "w-4 h-4 text-teal-500" }), _jsx("span", { className: "text-xs font-semibold text-teal-700 dark:text-teal-300 uppercase tracking-wider", children: "Group Balance" })] }), balances.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)))] })), showSettleMode && balances && (_jsxs("div", { className: "p-3 bg-teal-50 dark:bg-teal-900/20 mx-3 mt-3 rounded-xl border border-teal-100 dark:border-teal-800 flex-shrink-0", children: [_jsxs("div", { className: "flex items-center justify-between gap-2 mb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Users, { className: "w-4 h-4 text-teal-500" }), _jsx("span", { className: "text-xs font-semibold text-teal-700 dark:text-teal-300 uppercase tracking-wider", children: "Your Settlements" })] }), _jsx(Button, { onClick: () => setShowSettleMode(false), variant: "ghost", size: "sm", className: "h-6 w-6 p-0", children: _jsx(X, { className: "w-4 h-4" }) })] }), (() => {
213
+ const { owes, isOwed } = getUserSettlements();
214
+ const hasSettlements = owes.length > 0 || isOwed.length > 0;
215
+ return !hasSettlements ? (_jsx("p", { className: "text-xs text-gray-500 text-center py-4", children: "All settled up! \uD83C\uDF89" })) : (_jsxs("div", { className: "space-y-3", children: [owes.length > 0 && (_jsxs("div", { children: [_jsx("p", { className: "text-[10px] font-semibold text-red-600 dark:text-red-400 uppercase tracking-wider mb-2", children: "You Owe" }), _jsx("div", { className: "space-y-2", children: owes.map((s, i) => (_jsxs("div", { className: "flex items-center justify-between p-2.5 bg-white dark:bg-gray-800 rounded-lg border border-red-100 dark:border-red-900/30 hover:border-red-200 dark:hover:border-red-800 transition-colors", children: [_jsx("div", { className: "flex-1", children: _jsxs("p", { className: "text-xs text-gray-700 dark:text-gray-300", children: ["Pay ", _jsxs("span", { className: "font-semibold text-red-600 dark:text-red-400", children: ["\u20B9", s.amount.toFixed(0)] }), " to ", _jsx("span", { className: "font-semibold", children: s.to })] }) }), _jsxs(Button, { size: "sm", onClick: () => handleSettleSplit(s), disabled: settling === s.from + s.to, className: "text-[10px] text-white bg-red-500 hover:bg-red-600 dark:bg-red-600 dark:hover:bg-red-700 h-6 px-2 ml-2 flex-shrink-0", children: [_jsx(CheckCircle, { className: "w-3 h-3 mr-1" }), settling === s.from + s.to ? "..." : "Settle"] })] }, i))) })] })), isOwed.length > 0 && (_jsxs("div", { children: [_jsx("p", { className: "text-[10px] font-semibold text-green-600 dark:text-green-400 uppercase tracking-wider mb-2", children: "You Are Owed" }), _jsx("div", { className: "space-y-2", children: isOwed.map((s, i) => (_jsx("div", { className: "flex items-center justify-between p-2.5 bg-white dark:bg-gray-800 rounded-lg border border-green-100 dark:border-green-900/30 hover:border-green-200 dark:hover:border-green-800 transition-colors", children: _jsxs("p", { className: "text-xs text-gray-700 dark:text-gray-300", children: [_jsx("span", { className: "font-semibold", children: s.from }), " owes you", " ", _jsxs("span", { className: "font-semibold text-green-600 dark:text-green-400", children: ["\u20B9", s.amount.toFixed(0)] })] }) }, i))) })] }))] }));
216
+ })()] })), _jsx("div", { className: "flex-1 overflow-y-auto p-4 space-y-4 pt-0 mt-3", children: !isFeatureEnabled ? (_jsxs("div", { className: "flex flex-col items-center justify-center h-full py-12 px-6 text-center", children: [_jsx("div", { className: "mb-4 p-4 bg-teal-50 dark:bg-teal-900/20 rounded-full", children: _jsx(ReceiptText, { className: "w-12 h-12 text-teal-500" }) }), _jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-2", children: "Expenses Feature" }), _jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-4 max-w-sm", children: "Split costs fairly, track who owes whom, analyse expenses and settle balances easily. Perfect for shared trip expenses and group activities." }), isAdmin ? (_jsxs(Button, { onClick: onEnableFeature, className: "bg-gradient-to-r from-teal-500 to-cyan-600 hover:from-teal-600 hover:to-cyan-700 text-white", children: [_jsx(Unlock, { className: "w-4 h-4 mr-2" }), "Enable Feature"] })) : (_jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-500", children: [_jsx(Lock, { className: "w-4 h-4" }), "Ask admin to enable this feature"] }))] })) : loading ? (_jsx("div", { className: "flex justify-center py-8", children: _jsx("div", { className: "animate-spin w-5 h-5 border-2 border-teal-500 border-t-transparent rounded-full" }) })) : !showSettleMode && expenses.length === 0 && !isAdding ? (_jsx("div", { className: "text-center py-8 text-gray-400 text-sm", children: "No expenses yet" })) : (_jsxs(_Fragment, { children: [!showSettleMode && isAdding && (_jsxs("div", { className: "bg-teal-50 dark:bg-teal-900/20 p-3 rounded-xl border border-teal-100 dark:border-teal-800 space-y-3", children: [_jsx(Input, { autoFocus: true, value: newTitle, onChange: (e) => setNewTitle(e.target.value), placeholder: "What for?", className: "w-full bg-white dark:bg-gray-800 border-none outline-none rounded-lg p-2 text-sm text-gray-900 dark:text-white shadow-sm" }), _jsx("div", { className: "grid grid-cols-3 gap-2", children: _jsxs("div", { className: "col-span-2 relative", children: [_jsx("span", { className: "absolute left-2 top-2 text-gray-400 text-sm", children: "\u20B9" }), _jsx(Input, { type: "number", step: "0.01", value: newAmount, onChange: (e) => setNewAmount(e.target.value), placeholder: "0.00", className: "w-full bg-white dark:bg-gray-800 border-none outline-none rounded-lg p-2 pl-6 text-sm text-gray-900 dark:text-white shadow-sm" })] }) }), _jsxs(Button, { onClick: () => setShowMoreOptions(!showMoreOptions), variant: "secondary", className: "w-full flex items-center justify-between", children: [_jsx("span", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider", children: "More Options" }), _jsx(ChevronDown, { className: `w-4 h-4 text-gray-400 transition-transform ${showMoreOptions ? "rotate-180" : ""}` })] }), showMoreOptions && (_jsxs("div", { className: "bg-white dark:bg-gray-800 p-3 rounded-lg border border-gray-200 dark:border-gray-700 space-y-3", children: [_jsxs("div", { children: [_jsx(Label, { className: "text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider block mb-1", children: "Category" }), _jsx(Input, { placeholder: "Section (e.g., Day 1, Transport)", value: newCategory, onChange: (e) => setNewCategory(e.target.value), list: "section-suggestions", className: "text-sm" }), _jsx("datalist", { id: "section-suggestions", children: categories.map(category => (_jsx("option", { value: category }, category))) })] }), _jsx(SplitTypeSelector, { value: splitType, onChange: (type) => setSplitType(type) }), splitType !== "equal" && (_jsx(ParticipantSelector, { participants: splitParticipants, onChange: setSplitParticipants, onSplitsChange: setSplits, availableUsers: groupParticipants })), _jsx(SplitInputs, { splitType: splitType, totalAmount: parseFloat(newAmount) || 0, participants: splitParticipants, splits: splits, onChange: setSplits }), _jsxs("div", { children: [_jsx(Button, { onClick: handleGetLocation, disabled: gettingLocation || uploading, className: "w-full flex items-center justify-center gap-2 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 disabled:opacity-50 disabled:cursor-not-allowed border border-gray-200 dark:border-gray-600 rounded-lg p-2 text-sm text-gray-600 dark:text-gray-300 transition-colors", children: gettingLocation ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "w-4 h-4 animate-spin" }), _jsx("span", { children: "Getting..." })] })) : (_jsxs(_Fragment, { children: [_jsx(Navigation, { className: "w-4 h-4" }), _jsx("span", { children: "Add Location" })] })) }), newLocation && (_jsxs("div", { className: "mt-2 p-2 bg-white dark:bg-gray-700 rounded-lg border border-teal-200 dark:border-teal-800 flex items-center justify-between gap-2", children: [_jsxs("a", { href: newLocation, target: "_blank", rel: "noopener noreferrer", className: "text-xs text-blue-500 hover:underline truncate flex-1", children: [_jsx(MapPin, { className: "w-4 h-4 inline mr-1" }), "View Location"] }), _jsx("button", { onClick: () => setNewLocation(null), className: "text-red-500 hover:text-red-600", children: _jsx(X, { className: "w-4 h-4" }) })] }))] }), _jsxs("div", { children: [_jsxs("label", { className: "flex items-center gap-2 text-xs text-gray-500 cursor-pointer hover:text-teal-500 transition-colors bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg p-2", children: [_jsx(ImageIcon, { className: "w-4 h-4" }), receiptFile ? (_jsx("span", { className: "text-teal-500 truncate max-w-[150px]", children: receiptFile.name })) : ("Add Receipt"), _jsx(Input, { type: "file", className: "hidden", accept: "image/*", onChange: (e) => { var _a; return setReceiptFile(((_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0]) || null); } })] }), receiptFile && (_jsxs(Button, { onClick: () => setReceiptFile(null), className: "mt-2 w-full text-red-500 hover:text-red-600 text-xs flex items-center justify-center gap-2", variant: "ghost", children: [_jsx(Trash2, { className: "w-3 h-3" }), "Remove Receipt"] }))] }), _jsxs("div", { children: [_jsx("label", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider block mb-1", children: "Description" }), _jsx(Textarea, { value: newDescription, onChange: (e) => setNewDescription(e.target.value), placeholder: "Add details...", className: "w-full bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-lg p-2 text-xs text-gray-900 dark:text-white outline-none focus:border-teal-500 resize-none h-16" })] })] })), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: resetForm, disabled: uploading || gettingLocation, children: "Cancel" }), _jsxs(Button, { size: "sm", onClick: handleCreate, disabled: uploading || gettingLocation, className: "bg-gradient-to-r from-teal-500 to-cyan-600 hover:from-teal-600 hover:to-cyan-700 text-white", children: [uploading && _jsx(Loader2, { className: "w-3 h-3 mr-1 animate-spin" }), expenseId ? "Update" : "Add"] })] })] })), !showSettleMode &&
217
+ expenses.map((e) => {
218
+ var _a, _b, _c;
219
+ return e.title == "Internal_Settlement" ?
220
+ (_jsxs("div", { className: "w-full p-3 flex items-start gap-3 hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors group text-left", children: [_jsx("div", { className: "mt-1 p-2 bg-indigo-100 dark:bg-indigo-900/30 rounded-lg text-teal-500 flex-shrink-0", children: _jsx(PiggyBankIcon, { className: "w-4 h-4" }) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex justify-between items-start", children: [_jsxs("p", { className: "text-sm font-semibold text-gray-800 dark:text-gray-200 truncate", children: ["Paid to ", (_a = e.splits[0]) === null || _a === void 0 ? void 0 : _a.userId] }), _jsxs("p", { className: "text-sm font-bold text-gray-900 dark:text-white flex-shrink-0 ml-2", children: ["\u20B9", e.amount, " ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: e.currency })] })] }), _jsxs("div", { className: "text-[10px] text-gray-500 mt-1", children: ["by ", (_b = e.paidBy[0]) === null || _b === void 0 ? void 0 : _b.userId, " \u2022 ", new Date(e.date).toLocaleDateString()] })] })] }))
221
+ : (_jsxs("div", { className: "bg-gray-50 dark:bg-gray-800/30 border border-gray-100 dark:border-gray-800 rounded-xl transition-colors overflow-hidden", children: [_jsxs("button", { onClick: () => setExpandedExpenseId(expandedExpenseId === e._id ? null : e._id), className: "w-full p-3 flex items-start gap-3 hover:bg-gray-100 dark:hover:bg-gray-800/50 transition-colors group text-left", children: [_jsx("div", { className: "mt-1 p-2 bg-teal-100 dark:bg-teal-900/30 rounded-lg text-teal-500 flex-shrink-0", children: _jsx(Receipt, { className: "w-4 h-4" }) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex justify-between items-start", children: [_jsx("p", { className: "text-sm font-semibold text-gray-800 dark:text-gray-200 truncate", children: e.title }), _jsxs("p", { className: "text-sm font-bold text-gray-900 dark:text-white flex-shrink-0 ml-2", children: ["\u20B9", e.amount, " ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: e.currency })] })] }), _jsxs("div", { className: "text-[10px] text-gray-500 mt-1", children: ["by ", (_c = e.paidBy[0]) === null || _c === void 0 ? void 0 : _c.userId, " \u2022 ", new Date(e.date).toLocaleDateString()] })] }), _jsx(ChevronDown, { className: `w-4 h-4 text-gray-400 transition-transform flex-shrink-0 ${expandedExpenseId === e._id ? "rotate-180" : ""}` })] }), expandedExpenseId === e._id && (_jsxs("div", { className: "bg-white dark:bg-gray-700/50 border-t border-gray-200 dark:border-gray-700 p-3 space-y-3", children: [e.category && (_jsxs("div", { children: [_jsx("p", { className: "text-[10px] font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider mb-1", children: "Category" }), _jsx("p", { className: "text-xs text-gray-700 dark:text-gray-300", children: e.category })] })), (e.location || e.receipt) && _jsxs("div", { className: "flex flex-row w-full gap-2", children: [e.location && (_jsxs("div", { children: [_jsx("p", { className: "text-[10px] font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider mb-1", children: "Location" }), _jsxs("a", { href: e.location.startsWith("http")
222
+ ? e.location
223
+ : `https://maps.google.com/?q=${e.location}`, target: "_blank", rel: "noopener noreferrer", className: "text-xs text-blue-500 hover:underline flex items-center gap-1", children: [_jsx(MapPin, { className: "w-3 h-3" }), "View on Map"] })] })), e.receipt && (_jsxs("div", { children: [_jsx("p", { className: "text-[10px] font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider mb-1", children: "Receipt" }), _jsxs("a", { href: e.receipt, target: "_blank", rel: "noopener noreferrer", className: "text-xs text-blue-500 hover:underline flex items-center gap-1", children: [_jsx(Receipt, { className: "w-3 h-3" }), "View Receipt"] })] }))] }), e.description && (_jsxs("div", { children: [_jsx("p", { className: "text-[10px] font-semibold text-gray-600 dark:text-gray-400 uppercase tracking-wider mb-1", children: "Description" }), _jsx("p", { className: "text-xs text-gray-700 dark:text-gray-300 line-clamp-3", children: e.description })] })), _jsx(Button, { size: "sm", onClick: () => {
224
+ setSelectedExpenseForSplit(e);
225
+ setShowSplitModal(true);
226
+ }, className: "w-full text-teal-600 dark:text-teal-400 hover:text-teal-700 dark:hover:text-teal-300 text-xs border border-teal-200 dark:border-teal-800 bg-teal-50 dark:bg-teal-900/20", variant: "outline", children: "View Split Details" }), (e.createdBy === currentUserId || isAdmin) && (_jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { size: "sm", onClick: () => handleEdit(e), className: "flex-1 text-xs", variant: "outline", children: [_jsx(Pencil, { className: "w-3 h-3 mr-1" }), "Edit"] }), _jsxs(Button, { size: "sm", onClick: () => handleDelete(e._id), className: "flex-1 text-xs text-red-600 hover:text-red-700 border-red-200 dark:border-red-800 hover:bg-red-50 dark:hover:bg-red-900/20", variant: "outline", children: [_jsx(Trash2, { className: "w-3 h-3 mr-1" }), "Delete"] })] }))] }))] }, e._id));
227
+ })] })) }), isFeatureEnabled && !loading && (_jsx("div", { className: "p-4 border-t border-gray-200 dark:border-gray-700 flex-shrink-0 space-y-2", children: showSettleMode ? (_jsx(Button, { onClick: () => setShowSettleMode(false), className: "w-full bg-gradient-to-r from-teal-500 to-cyan-600 hover:from-teal-600 hover:to-cyan-700 text-white rounded-lg", children: "Back to Expenses" })) : (_jsxs(_Fragment, { children: [!isAdding && (_jsxs(Button, { onClick: () => {
228
+ setExpenseId(null);
229
+ setIsAdding(true);
230
+ setSplitParticipants([]);
231
+ }, className: "w-full bg-gradient-to-r from-teal-500 to-cyan-600 hover:from-teal-600 hover:to-cyan-700 text-white rounded-lg flex items-center justify-center gap-2", children: [_jsx(Plus, { className: "w-4 h-4" }), "Add Expense"] })), userOwesAmount() > 0 && !isAdding && (_jsxs(Button, { onClick: () => setShowSettleMode(true), variant: "outline", className: "w-full rounded-lg border-teal-500 text-teal-600 hover:bg-teal-50 dark:hover:bg-teal-900/10", children: [_jsx(CreditCard, { className: "w-4 h-4 mr-2" }), "Settle Debts (\u20B9", userOwesAmount().toFixed(2), ")"] }))] })) })), showSplitModal && selectedExpenseForSplit && (_jsx("div", { className: "fixed inset-0 bg-black/50 flex items-end z-50", children: _jsxs("div", { className: "w-full bg-white dark:bg-gray-900 rounded-t-2xl p-4 max-h-96 overflow-y-auto", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: "Split Details" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => {
232
+ setShowSplitModal(false);
233
+ setSelectedExpenseForSplit(null);
234
+ }, children: _jsx(X, { className: "w-4 h-4" }) })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "bg-teal-50 dark:bg-teal-900/20 p-3 rounded-lg border border-teal-100 dark:border-teal-800", children: [_jsx("p", { className: "text-xs font-semibold text-teal-700 dark:text-teal-300 uppercase tracking-wider mb-2", children: selectedExpenseForSplit.title }), _jsxs("p", { className: "text-sm font-bold text-teal-900 dark:text-teal-100", children: ["\u20B9", selectedExpenseForSplit.amount, " ", selectedExpenseForSplit.currency] }), _jsxs("p", { className: "text-[10px] text-teal-600 dark:text-teal-400 mt-1", children: ["Split Type: ", _jsx("span", { className: "font-semibold capitalize", children: selectedExpenseForSplit.splitType })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("p", { className: "text-xs font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider", children: "Individual Splits" }), selectedExpenseForSplit.splits.map((split, idx) => (_jsxs("div", { className: "flex items-center justify-between p-2 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-100 dark:border-gray-700", children: [_jsxs("div", { children: [_jsx("p", { className: "text-xs font-medium text-gray-700 dark:text-gray-300", children: split.userId }), split.percentage && (_jsxs("p", { className: "text-[10px] text-gray-500", children: [split.percentage, "%"] })), split.shares && (_jsxs("p", { className: "text-[10px] text-gray-500", children: [split.shares, " shares"] }))] }), _jsxs("p", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: ["\u20B9", split.amount.toFixed(2)] })] }, idx)))] })] })] }) })), _jsx(ExpenseAnalyticsDialog, { conversationId: conversationId, open: showAnalytics, onClose: () => setShowAnalytics(false) })] }));
215
235
  }
@@ -1,16 +1,18 @@
1
- import type { Message } from "@myrjfa/state/dist/lib/models/chat";
1
+ import type { Message, Participant } from "@myrjfa/state/dist/lib/models/chat";
2
2
  interface MessageBubbleProps {
3
3
  message: Message;
4
+ participants: Participant[];
4
5
  currentUserId: string;
5
6
  showSender?: boolean;
6
7
  senderProfilePic?: string;
7
- onEdit?: (messageId: string, text: string) => void;
8
+ onEdit?: (message: Message) => void;
8
9
  onDelete?: (messageId: string) => void;
9
10
  onReply?: (message: Message) => void;
10
11
  onReactionAdd?: (messageId: string, emoji: string) => void;
11
12
  onReactionRemove?: (messageId: string) => void;
12
13
  onScrollToReply?: (messageId: string) => void;
14
+ onVote?: (pollId: string, optionIndex: number) => void;
13
15
  }
14
- export declare function MessageBubble({ message, currentUserId, showSender, senderProfilePic, onEdit, onDelete, onReply, onReactionAdd, onReactionRemove, onScrollToReply }: MessageBubbleProps): import("react/jsx-runtime").JSX.Element;
16
+ export declare function MessageBubble({ message, participants, currentUserId, showSender, senderProfilePic, onEdit, onDelete, onReply, onReactionAdd, onReactionRemove, onScrollToReply, onVote }: MessageBubbleProps): import("react/jsx-runtime").JSX.Element;
15
17
  export {};
16
18
  //# sourceMappingURL=MessageBubble.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../src/chat/MessageBubble.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAUlE,UAAU,kBAAkB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAWD,wBAAgB,aAAa,CAAC,EAC1B,OAAO,EACP,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,MAAM,EACN,QAAQ,EACR,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,eAAe,EAClB,EAAE,kBAAkB,2CAwdpB"}
1
+ {"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../src/chat/MessageBubble.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAQ,MAAM,oCAAoC,CAAC;AASrF,UAAU,kBAAkB;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1D;AAWD,wBAAgB,aAAa,CAAC,EAC1B,OAAO,EACP,YAAY,EACZ,aAAa,EACb,UAAU,EACV,gBAAgB,EAChB,MAAM,EACN,QAAQ,EACR,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,MAAM,EACT,EAAE,kBAAkB,2CA0mBpB"}