@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.
- package/dist/chat/ChatMain.d.ts.map +1 -1
- package/dist/chat/ChatMain.js +173 -145
- package/dist/chat/ChatSidebar.js +7 -8
- package/dist/chat/ConversationList.d.ts.map +1 -1
- package/dist/chat/ConversationList.js +20 -18
- package/dist/chat/ExpensePanel.d.ts +4 -1
- package/dist/chat/ExpensePanel.d.ts.map +1 -1
- package/dist/chat/ExpensePanel.js +215 -195
- package/dist/chat/MessageBubble.d.ts +5 -3
- package/dist/chat/MessageBubble.d.ts.map +1 -1
- package/dist/chat/MessageBubble.js +185 -103
- package/dist/chat/MessageInput.d.ts +8 -1
- package/dist/chat/MessageInput.d.ts.map +1 -1
- package/dist/chat/MessageInput.js +182 -165
- package/dist/chat/MilestonePanel.d.ts +4 -1
- package/dist/chat/MilestonePanel.d.ts.map +1 -1
- package/dist/chat/MilestonePanel.js +150 -150
- package/dist/chat/components/ParticipantSelector.d.ts +11 -0
- package/dist/chat/components/ParticipantSelector.d.ts.map +1 -0
- package/dist/chat/components/ParticipantSelector.js +17 -0
- package/dist/chat/components/SplitInputs.d.ts +11 -0
- package/dist/chat/components/SplitInputs.d.ts.map +1 -0
- package/dist/chat/components/SplitInputs.js +83 -0
- package/dist/chat/components/SplitTypeSelector.d.ts +8 -0
- package/dist/chat/components/SplitTypeSelector.d.ts.map +1 -0
- package/dist/chat/components/SplitTypeSelector.js +13 -0
- package/dist/chat/dialogs/AddMemberDialog.js +30 -90
- package/dist/chat/dialogs/CameraDialog.js +38 -86
- package/dist/chat/dialogs/CreatePollDialog.d.ts +8 -0
- package/dist/chat/dialogs/CreatePollDialog.d.ts.map +1 -0
- package/dist/chat/dialogs/CreatePollDialog.js +38 -0
- package/dist/chat/dialogs/ExpenseAnalyticsDialog.d.ts +19 -0
- package/dist/chat/dialogs/ExpenseAnalyticsDialog.d.ts.map +1 -0
- package/dist/chat/dialogs/ExpenseAnalyticsDialog.js +52 -0
- package/dist/chat/dialogs/GroupInfoDialog.d.ts.map +1 -1
- package/dist/chat/dialogs/GroupInfoDialog.js +55 -92
- package/dist/chat/dialogs/GroupInfoViewDialog.js +5 -6
- package/dist/chat/dialogs/NewChatDialog.d.ts.map +1 -1
- package/dist/chat/dialogs/NewChatDialog.js +49 -113
- package/dist/chat/dialogs/NewGroupDialog.d.ts.map +1 -1
- package/dist/chat/dialogs/NewGroupDialog.js +49 -92
- package/dist/chat/dialogs/ParticipantsDialog.d.ts.map +1 -1
- package/dist/chat/dialogs/ParticipantsDialog.js +68 -118
- package/dist/chat/dialogs/UserProfileDialog.d.ts +10 -0
- package/dist/chat/dialogs/UserProfileDialog.d.ts.map +1 -0
- package/dist/chat/dialogs/UserProfileDialog.js +100 -0
- package/dist/dashboard/action-buttons.js +4 -5
- package/dist/dashboard/app-sidebar.js +46 -128
- package/dist/dashboard/card-header.js +1 -2
- package/dist/dashboard/dashboard-components.js +3 -5
- package/dist/dashboard/data-table.js +21 -33
- package/dist/dashboard/nav-main.js +4 -5
- package/dist/dashboard/nav-secondary.js +2 -25
- package/dist/dashboard/stats-card.js +1 -2
- package/dist/dashboard/table.js +8 -54
- package/dist/header/mobile-nav-item.js +1 -2
- package/dist/header/mobile-navbar.js +8 -9
- package/dist/header/nav-item.js +1 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/main/footer.js +5 -9
- package/dist/main/navbar.js +53 -136
- package/dist/main/paginationBar.js +12 -14
- package/dist/main/portfolio/portfolioItem.js +1 -2
- package/dist/main/portfolio/portfolioPopup.js +16 -17
- package/dist/main/review-page.d.ts.map +1 -1
- package/dist/main/review-page.js +131 -198
- package/dist/misc/accordion.js +8 -34
- package/dist/misc/alert-dialog.js +22 -55
- package/dist/misc/alert.js +7 -32
- package/dist/misc/alertdialogclaude.js +50 -125
- package/dist/misc/avatar.js +6 -31
- package/dist/misc/badge.js +4 -27
- package/dist/misc/button.js +4 -27
- package/dist/misc/card.js +12 -40
- package/dist/misc/carousel.js +37 -61
- package/dist/misc/checkbox.js +2 -25
- package/dist/misc/command.js +18 -49
- package/dist/misc/datepicker.js +66 -76
- package/dist/misc/dialog.js +20 -52
- package/dist/misc/dropdown-menu.js +30 -67
- package/dist/misc/faq.js +2 -3
- package/dist/misc/file-preview.js +43 -97
- package/dist/misc/filter.d.ts.map +1 -1
- package/dist/misc/filter.js +18 -21
- package/dist/misc/filterBar.js +136 -160
- package/dist/misc/form.js +39 -60
- package/dist/misc/image-cropper.d.ts +10 -0
- package/dist/misc/image-cropper.d.ts.map +1 -0
- package/dist/misc/image-cropper.js +44 -0
- package/dist/misc/input.js +2 -25
- package/dist/misc/label.js +2 -25
- package/dist/misc/line-carousel.js +3 -13
- package/dist/misc/multiImageUploader.js +2 -3
- package/dist/misc/newsLetter.js +18 -78
- package/dist/misc/notification.d.ts +1 -2
- package/dist/misc/notification.d.ts.map +1 -1
- package/dist/misc/notification.js +89 -125
- package/dist/misc/pagination.js +16 -45
- package/dist/misc/password-eye.js +2 -3
- package/dist/misc/popover.d.ts.map +1 -1
- package/dist/misc/popover.js +8 -34
- package/dist/misc/progress.js +2 -25
- package/dist/misc/scroll-area.js +5 -29
- package/dist/misc/searchBar.js +27 -31
- package/dist/misc/searchBox.js +18 -22
- package/dist/misc/select.d.ts.map +1 -1
- package/dist/misc/select.js +22 -54
- package/dist/misc/separator.js +2 -25
- package/dist/misc/share.js +48 -104
- package/dist/misc/sheet.js +21 -53
- package/dist/misc/sidebar.js +112 -153
- package/dist/misc/skeleton.js +2 -25
- package/dist/misc/slider.js +3 -26
- package/dist/misc/starRating.js +5 -15
- package/dist/misc/stateCard.js +7 -8
- package/dist/misc/switch.d.ts +5 -0
- package/dist/misc/switch.d.ts.map +1 -0
- package/dist/misc/switch.js +8 -0
- package/dist/misc/tabs.js +8 -34
- package/dist/misc/textarea.js +2 -25
- package/dist/misc/tile.js +31 -92
- package/dist/misc/tooltip.js +8 -34
- package/dist/profile/socialLink.d.ts +1 -1
- package/dist/profile/socialLink.d.ts.map +1 -1
- package/dist/profile/socialLink.js +53 -69
- package/dist/utils/permissions.d.ts +7 -0
- package/dist/utils/permissions.d.ts.map +1 -0
- package/dist/utils/permissions.js +38 -0
- 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,
|
|
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 {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
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(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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?: (
|
|
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;
|
|
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"}
|