@powerhousedao/contributor-billing 0.1.5 → 0.1.6
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/editors/billing-statement/lineItemTags/lineItemTags.js +1 -1
- package/dist/editors/expense-report/components/AddBillingStatementModal.js +1 -1
- package/dist/editors/expense-report/components/AggregatedExpensesTable.d.ts.map +1 -1
- package/dist/editors/expense-report/components/AggregatedExpensesTable.js +128 -10
- package/dist/editors/expense-report/components/ExpenseReportPDF.d.ts.map +1 -1
- package/dist/editors/expense-report/components/ExpenseReportPDF.js +7 -7
- package/dist/editors/expense-report/components/WalletsTable.d.ts.map +1 -1
- package/dist/editors/expense-report/components/WalletsTable.js +25 -13
- package/dist/editors/invoice/legalEntity/bankSection.d.ts.map +1 -1
- package/dist/editors/invoice/legalEntity/bankSection.js +18 -18
- package/dist/editors/invoice/lineItemTags/lineItemTags.js +1 -1
- package/dist/style.css +58 -0
- package/package.json +2 -1
|
@@ -24,7 +24,7 @@ export function LineItemTagsTable({ lineItems, onClose, dispatch, }) {
|
|
|
24
24
|
id: item.id,
|
|
25
25
|
description: e.target.value,
|
|
26
26
|
}));
|
|
27
|
-
}, className: "w-full text-xs" }) }), _jsx("td", { className: "border-b border-gray-200 p-2 w-50", children: _jsx(DatePicker, { name: "period", dateFormat: "YYYY-MM", autoClose: true, placeholder: "Select Period", value: item.lineItemTag.find((tag) => tag.dimension === "accounting-period")?.label || "", onChange: (e) => dispatch(actions.editLineItemTag({
|
|
27
|
+
}, className: "w-full text-xs" }) }), _jsx("td", { className: "border-b border-gray-200 p-2 w-50", children: _jsx(DatePicker, { name: "period", dateFormat: "YYYY-MM-DD", autoClose: true, placeholder: "Select Period", value: item.lineItemTag.find((tag) => tag.dimension === "accounting-period")?.label || "", onChange: (e) => dispatch(actions.editLineItemTag({
|
|
28
28
|
lineItemId: item.id,
|
|
29
29
|
dimension: "accounting-period",
|
|
30
30
|
value: new Date(e.target.value)
|
|
@@ -191,5 +191,5 @@ export function AddBillingStatementModal({ isOpen, onClose, walletAddress, dispa
|
|
|
191
191
|
: "border-gray-300 dark:border-gray-600"}`, children: (isSelected || isAlreadyAdded) && (_jsx(Check, { className: "text-white", size: 14 })) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-medium text-gray-900 dark:text-white truncate", children: statement.name }), _jsxs("span", { className: "text-xs text-gray-500 dark:text-gray-400 px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded", children: [lineItemCount, " items"] }), isAlreadyAdded && (_jsx("span", { className: "text-xs text-amber-600 dark:text-amber-400 px-2 py-1 bg-amber-50 dark:bg-amber-900/20 rounded font-medium", children: "Already included" }))] }), _jsxs("div", { className: "flex items-center gap-4 mt-1 text-sm text-gray-600 dark:text-gray-400", children: [_jsxs("span", { children: ["Total: ", formatCurrency(totalCash)] }), _jsxs("span", { className: "text-xs font-mono", children: [statement.id.substring(0, 8), "..."] })] })] })] }, statement.id));
|
|
192
192
|
}) })) : (_jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-gray-500 dark:text-gray-400", children: [_jsx(FileText, { size: 48, className: "mb-4 opacity-50" }), _jsx("p", { className: "text-sm", children: searchTerm
|
|
193
193
|
? "No billing statements found matching your search"
|
|
194
|
-
: "No billing statements available in this drive" })] })) }), _jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50", children: [_jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400", children: [selectedStatements.size, " statement", selectedStatements.size !== 1 ? "s" : "", " selected"] }), _jsxs("div", { className: "flex gap-3", children: [_jsx(Button, { onClick: onClose,
|
|
194
|
+
: "No billing statements available in this drive" })] })) }), _jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50", children: [_jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400", children: [selectedStatements.size, " statement", selectedStatements.size !== 1 ? "s" : "", " selected"] }), _jsxs("div", { className: "flex gap-3", children: [_jsx(Button, { onClick: onClose, variant: "secondary", children: "CANCEL" }), _jsxs(Button, { onClick: handleAddStatements, disabled: selectedStatements.size === 0, className: "bg-green-600 hover:bg-green-700 text-white", children: ["ADD", " ", selectedStatements.size > 0 && `(${selectedStatements.size})`] })] })] })] })] }));
|
|
195
195
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AggregatedExpensesTable.d.ts","sourceRoot":"","sources":["../../../../editors/expense-report/components/AggregatedExpensesTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAY,MAAM,sDAAsD,CAAC;
|
|
1
|
+
{"version":3,"file":"AggregatedExpensesTable.d.ts","sourceRoot":"","sources":["../../../../editors/expense-report/components/AggregatedExpensesTable.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAY,MAAM,sDAAsD,CAAC;AAM5G,UAAU,4BAA4B;IACpC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CACjC;AAQD,wBAAgB,uBAAuB,CAAC,EACtC,OAAO,EACP,MAAM,EACN,WAAW,EACX,SAAS,EACT,QAAQ,GACT,EAAE,4BAA4B,kDAmzB9B"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, useState } from "react";
|
|
2
|
+
import { useMemo, useState, useRef, useEffect } from "react";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { actions } from "../../../document-models/expense-report/index.js";
|
|
5
|
-
import { Textarea } from "@powerhousedao/document-engineering";
|
|
5
|
+
import { Textarea, Select, Button } from "@powerhousedao/document-engineering";
|
|
6
|
+
import { Plus, Trash2 } from "lucide-react";
|
|
7
|
+
import { generateId } from "document-model";
|
|
6
8
|
export function AggregatedExpensesTable({ wallets, groups, periodStart, periodEnd, dispatch, }) {
|
|
7
9
|
// State for active tab (selected wallet)
|
|
8
10
|
const [activeWalletIndex, setActiveWalletIndex] = useState(0);
|
|
@@ -13,6 +15,122 @@ export function AggregatedExpensesTable({ wallets, groups, periodStart, periodEn
|
|
|
13
15
|
// State for editing numeric fields
|
|
14
16
|
const [editingField, setEditingField] = useState(null);
|
|
15
17
|
const [editingValue, setEditingValue] = useState("");
|
|
18
|
+
// State for adding new line item
|
|
19
|
+
const [isAddingLineItem, setIsAddingLineItem] = useState(false);
|
|
20
|
+
const [selectedGroupId, setSelectedGroupId] = useState("");
|
|
21
|
+
const [duplicateCategoryError, setDuplicateCategoryError] = useState("");
|
|
22
|
+
// State for delete confirmation modal
|
|
23
|
+
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
|
|
24
|
+
const [lineItemToDelete, setLineItemToDelete] = useState(null);
|
|
25
|
+
// Ref for line item editor to scroll into view
|
|
26
|
+
const lineItemEditorRef = useRef(null);
|
|
27
|
+
// Scroll to bottom of page when editor opens
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (isAddingLineItem && lineItemEditorRef.current) {
|
|
30
|
+
// Use a slight delay to ensure the editor is rendered
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
// Find the scrollable container by traversing up from the ref
|
|
33
|
+
let element = lineItemEditorRef.current?.parentElement;
|
|
34
|
+
while (element) {
|
|
35
|
+
const style = window.getComputedStyle(element);
|
|
36
|
+
const isScrollable = style.overflow === 'auto' || style.overflow === 'scroll' ||
|
|
37
|
+
style.overflowY === 'auto' || style.overflowY === 'scroll';
|
|
38
|
+
if (isScrollable && element.scrollHeight > element.clientHeight) {
|
|
39
|
+
// Found the scrollable container, scroll to bottom
|
|
40
|
+
element.scrollTo({ top: element.scrollHeight, behavior: "smooth" });
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
element = element.parentElement;
|
|
44
|
+
}
|
|
45
|
+
}, 150);
|
|
46
|
+
}
|
|
47
|
+
}, [isAddingLineItem]);
|
|
48
|
+
// Get existing category IDs for the active wallet
|
|
49
|
+
const existingCategoryIds = useMemo(() => {
|
|
50
|
+
const wallet = wallets[activeWalletIndex];
|
|
51
|
+
if (!wallet || !wallet.lineItems)
|
|
52
|
+
return new Set();
|
|
53
|
+
return new Set(wallet.lineItems
|
|
54
|
+
.filter((item) => item !== null && item !== undefined)
|
|
55
|
+
.map(item => item.group)
|
|
56
|
+
.filter((group) => !!group));
|
|
57
|
+
}, [wallets, activeWalletIndex]);
|
|
58
|
+
// Create group options for Select component
|
|
59
|
+
const groupOptions = useMemo(() => {
|
|
60
|
+
return groups.map((group) => ({
|
|
61
|
+
value: group.id,
|
|
62
|
+
label: group.label || group.id,
|
|
63
|
+
}));
|
|
64
|
+
}, [groups]);
|
|
65
|
+
// Handle category selection with duplicate check
|
|
66
|
+
const handleCategoryChange = (value) => {
|
|
67
|
+
setSelectedGroupId(value);
|
|
68
|
+
// Check if category already exists
|
|
69
|
+
if (existingCategoryIds.has(value)) {
|
|
70
|
+
const categoryLabel = groups.find(g => g.id === value)?.label || value;
|
|
71
|
+
setDuplicateCategoryError(`"${categoryLabel}" already exists in this wallet. Please select a different category.`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
setDuplicateCategoryError("");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
// Handle saving new line item
|
|
78
|
+
const handleSaveLineItem = () => {
|
|
79
|
+
const wallet = wallets[activeWalletIndex];
|
|
80
|
+
if (!wallet || !wallet.wallet || !selectedGroupId)
|
|
81
|
+
return;
|
|
82
|
+
// Prevent saving if duplicate
|
|
83
|
+
if (existingCategoryIds.has(selectedGroupId)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const newLineItem = {
|
|
87
|
+
id: generateId(),
|
|
88
|
+
label: groups.find(g => g.id === selectedGroupId)?.label || "",
|
|
89
|
+
group: selectedGroupId,
|
|
90
|
+
budget: 0,
|
|
91
|
+
actuals: 0,
|
|
92
|
+
forecast: 0,
|
|
93
|
+
payments: 0,
|
|
94
|
+
comments: "",
|
|
95
|
+
};
|
|
96
|
+
dispatch(actions.addLineItem({
|
|
97
|
+
wallet: wallet.wallet,
|
|
98
|
+
lineItem: newLineItem,
|
|
99
|
+
}));
|
|
100
|
+
// Reset state
|
|
101
|
+
setIsAddingLineItem(false);
|
|
102
|
+
setSelectedGroupId("");
|
|
103
|
+
setDuplicateCategoryError("");
|
|
104
|
+
};
|
|
105
|
+
// Handle canceling new line item
|
|
106
|
+
const handleCancelLineItem = () => {
|
|
107
|
+
setIsAddingLineItem(false);
|
|
108
|
+
setSelectedGroupId("");
|
|
109
|
+
setDuplicateCategoryError("");
|
|
110
|
+
};
|
|
111
|
+
// Handle opening delete confirmation modal
|
|
112
|
+
const handleDeleteLineItem = (lineItemId, lineItemLabel) => {
|
|
113
|
+
setLineItemToDelete({ id: lineItemId, label: lineItemLabel });
|
|
114
|
+
setDeleteModalOpen(true);
|
|
115
|
+
};
|
|
116
|
+
// Handle confirming deletion
|
|
117
|
+
const handleConfirmDelete = () => {
|
|
118
|
+
const wallet = wallets[activeWalletIndex];
|
|
119
|
+
if (!wallet || !wallet.wallet || !lineItemToDelete)
|
|
120
|
+
return;
|
|
121
|
+
dispatch(actions.removeLineItem({
|
|
122
|
+
wallet: wallet.wallet,
|
|
123
|
+
lineItemId: lineItemToDelete.id,
|
|
124
|
+
}));
|
|
125
|
+
// Close modal and reset state
|
|
126
|
+
setDeleteModalOpen(false);
|
|
127
|
+
setLineItemToDelete(null);
|
|
128
|
+
};
|
|
129
|
+
// Handle canceling deletion
|
|
130
|
+
const handleCancelDelete = () => {
|
|
131
|
+
setDeleteModalOpen(false);
|
|
132
|
+
setLineItemToDelete(null);
|
|
133
|
+
};
|
|
16
134
|
// Format period for title
|
|
17
135
|
const periodTitle = useMemo(() => {
|
|
18
136
|
if (!periodStart)
|
|
@@ -215,7 +333,7 @@ export function AggregatedExpensesTable({ wallets, groups, periodStart, periodEn
|
|
|
215
333
|
? "border-green-500 text-green-600 dark:text-green-400"
|
|
216
334
|
: "border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-600"}
|
|
217
335
|
`, children: wallet.name || formatWalletAddress(wallet.wallet || "") }, wallet.wallet || index));
|
|
218
|
-
}) }) }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "min-w-full divide-y divide-gray-200 dark:divide-gray-700", children: [_jsx("thead", { className: "bg-gray-50 dark:bg-gray-800", children: _jsxs("tr", { children: [_jsx("th", { className: "
|
|
336
|
+
}) }) }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "min-w-full divide-y divide-gray-200 dark:divide-gray-700", children: [_jsx("thead", { className: "bg-gray-50 dark:bg-gray-800", children: _jsxs("tr", { children: [_jsx("th", { className: "pl-6 pr-3 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { onClick: () => setIsAddingLineItem(true), className: "inline-flex items-center justify-center w-6 h-6 text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 bg-blue-50 dark:bg-blue-900/20 hover:bg-blue-100 dark:hover:bg-blue-900/30 rounded-md transition-colors", children: _jsx(Plus, { size: 14 }) }), _jsx("span", { children: "Expense Category" })] }) }), _jsx("th", { className: "px-3 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Mthly Budget" }), _jsx("th", { className: "px-3 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Forecast" }), _jsx("th", { className: "px-3 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Actuals" }), _jsx("th", { className: "px-3 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Difference" }), _jsx("th", { className: "px-3 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider w-96", children: "Comments" }), _jsx("th", { className: "px-3 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider w-32", children: "Payments" }), _jsx("th", { className: "px-2 py-3 text-center text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider w-16", children: "Actions" })] }) }), _jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-700", children: [sortedParentKeys.map((parentKey) => {
|
|
219
337
|
const items = groupedAndAggregatedItems.get(parentKey) || [];
|
|
220
338
|
if (items.length === 0)
|
|
221
339
|
return null;
|
|
@@ -223,7 +341,7 @@ export function AggregatedExpensesTable({ wallets, groups, periodStart, periodEn
|
|
|
223
341
|
const parentLabel = parentKey === "uncategorized"
|
|
224
342
|
? "Uncategorised"
|
|
225
343
|
: items[0]?.parentGroupLabel || "Other";
|
|
226
|
-
return (_jsxs(React.Fragment, { children: [_jsx("tr", { className: "bg-gray-100 dark:bg-gray-800", children: _jsx("td", { colSpan:
|
|
344
|
+
return (_jsxs(React.Fragment, { children: [_jsx("tr", { className: "bg-gray-100 dark:bg-gray-800", children: _jsx("td", { colSpan: 8, className: "px-6 py-3 text-sm font-bold text-gray-900 dark:text-white", children: parentLabel }) }), items.map((item) => {
|
|
227
345
|
if (!item)
|
|
228
346
|
return null;
|
|
229
347
|
const difference = item.forecast - item.actuals;
|
|
@@ -244,7 +362,7 @@ export function AggregatedExpensesTable({ wallets, groups, periodStart, periodEn
|
|
|
244
362
|
}
|
|
245
363
|
return (_jsx("div", { className: "group cursor-pointer text-right", onClick: () => handleStartFieldEdit(item.lineItemId, field, value), children: _jsx("span", { className: "group-hover:bg-blue-50 dark:group-hover:bg-blue-900/20 inline-block px-1 py-0.5 rounded transition-colors min-w-[4rem]", children: formatNumber(value) }) }));
|
|
246
364
|
};
|
|
247
|
-
return (_jsxs("tr", { className: "bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors align-top", children: [_jsx("td", { className: "
|
|
365
|
+
return (_jsxs("tr", { className: "bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors align-top", children: [_jsx("td", { className: "pl-6 pr-3 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-white", children: item.groupLabel }), _jsx("td", { className: "px-3 py-3 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: renderEditableCell("budget", item.budget) }), _jsx("td", { className: "px-3 py-3 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: renderEditableCell("forecast", item.forecast) }), _jsx("td", { className: "px-3 py-3 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: renderEditableCell("actuals", item.actuals) }), _jsx("td", { className: `px-3 py-3 whitespace-nowrap text-right text-sm font-medium ${difference < 0
|
|
248
366
|
? "text-red-600 dark:text-red-400"
|
|
249
367
|
: "text-gray-900 dark:text-white"}`, children: formatNumber(difference) }), _jsx("td", { className: "px-3 py-3 text-sm w-96", children: isEditingComment ? (_jsx(Textarea, { value: editingComment, onChange: (e) => setEditingComment(e.target.value), placeholder: "Add comment...", autoExpand: true, multiline: true, onKeyDown: (e) => {
|
|
250
368
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
@@ -258,11 +376,11 @@ export function AggregatedExpensesTable({ wallets, groups, periodStart, periodEn
|
|
|
258
376
|
e.preventDefault();
|
|
259
377
|
handleSaveComment();
|
|
260
378
|
}
|
|
261
|
-
}, onBlur: handleSaveComment, autoFocus: true, className: "w-full px-2 py-1 text-sm border border-blue-500 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-white max-h-32 overflow-y-auto" })) : (_jsx("div", { className: "group cursor-pointer w-full max-h-20 overflow-hidden", onClick: () => handleStartEdit(item.lineItemId, item.comment), title: item.comment || "No comments", children: _jsx("span", { className: "group-hover:bg-blue-50 dark:group-hover:bg-blue-900/20 px-1 py-0.5 rounded transition-colors block text-gray-600 dark:text-gray-400 break-words", children: item.comment || "No comments" }) })) }), _jsx("td", { className: "px-3 py-3 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white w-32", children: renderEditableCell("payments", item.payments) })] }, item.lineItemId));
|
|
262
|
-
}), _jsxs("tr", { className: "bg-gray-50 dark:bg-gray-800/50 font-semibold align-top", children: [_jsx("td", { className: "
|
|
379
|
+
}, onBlur: handleSaveComment, autoFocus: true, className: "w-full px-2 py-1 text-sm border border-blue-500 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-white max-h-32 overflow-y-auto" })) : (_jsx("div", { className: "group cursor-pointer w-full max-h-20 overflow-hidden", onClick: () => handleStartEdit(item.lineItemId, item.comment), title: item.comment || "No comments", children: _jsx("span", { className: "group-hover:bg-blue-50 dark:group-hover:bg-blue-900/20 px-1 py-0.5 rounded transition-colors block text-gray-600 dark:text-gray-400 break-words", children: item.comment || "No comments" }) })) }), _jsx("td", { className: "px-3 py-3 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white w-32", children: renderEditableCell("payments", item.payments) }), _jsx("td", { className: "px-2 py-3 whitespace-nowrap text-center w-16", children: _jsx("button", { onClick: () => handleDeleteLineItem(item.lineItemId, item.groupLabel), className: "inline-flex items-center justify-center p-0.5 text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300 hover:bg-red-50 dark:hover:bg-red-900/20 rounded transition-colors", title: "Delete line item", children: _jsx(Trash2, { size: 14 }) }) })] }, item.lineItemId));
|
|
380
|
+
}), _jsxs("tr", { className: "bg-gray-50 dark:bg-gray-800/50 font-semibold align-top", children: [_jsx("td", { className: "pl-6 pr-3 py-3 whitespace-nowrap text-sm text-gray-900 dark:text-white", children: "Subtotal" }), _jsx("td", { className: "px-3 py-3 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: _jsx("div", { className: "text-right", children: _jsx("span", { className: "inline-block px-1 py-0.5 min-w-[4rem]", children: formatNumber(subtotals.budget) }) }) }), _jsx("td", { className: "px-3 py-3 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: _jsx("div", { className: "text-right", children: _jsx("span", { className: "inline-block px-1 py-0.5 min-w-[4rem]", children: formatNumber(subtotals.forecast) }) }) }), _jsx("td", { className: "px-3 py-3 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: _jsx("div", { className: "text-right", children: _jsx("span", { className: "inline-block px-1 py-0.5 min-w-[4rem]", children: formatNumber(subtotals.actuals) }) }) }), _jsx("td", { className: `px-3 py-3 whitespace-nowrap text-right text-sm font-bold ${subtotals.difference < 0
|
|
263
381
|
? "text-red-600 dark:text-red-400"
|
|
264
|
-
: "text-gray-900 dark:text-white"}`, children: formatNumber(subtotals.difference) }), _jsx("td", { className: "px-3 py-3" }), _jsx("td", { className: "px-3 py-3 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white w-32", children: _jsx("div", { className: "text-right", children: _jsx("span", { className: "inline-block px-1 py-0.5 min-w-[4rem]", children: formatNumber(subtotals.payments) }) }) })] })] }, parentKey));
|
|
265
|
-
}), _jsxs("tr", { className: "bg-gray-100 dark:bg-gray-800 font-bold align-top", children: [_jsx("td", { className: "
|
|
382
|
+
: "text-gray-900 dark:text-white"}`, children: formatNumber(subtotals.difference) }), _jsx("td", { className: "px-3 py-3" }), _jsx("td", { className: "px-3 py-3 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white w-32", children: _jsx("div", { className: "text-right", children: _jsx("span", { className: "inline-block px-1 py-0.5 min-w-[4rem]", children: formatNumber(subtotals.payments) }) }) }), _jsx("td", { className: "px-2 py-3" })] })] }, parentKey));
|
|
383
|
+
}), _jsxs("tr", { className: "bg-gray-100 dark:bg-gray-800 font-bold align-top", children: [_jsx("td", { className: "pl-6 pr-3 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-white", children: "Total" }), _jsx("td", { className: "px-3 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: _jsx("div", { className: "text-right", children: _jsx("span", { className: "inline-block px-1 py-0.5 min-w-[4rem]", children: formatNumber(grandTotals.budget) }) }) }), _jsx("td", { className: "px-3 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: _jsx("div", { className: "text-right", children: _jsx("span", { className: "inline-block px-1 py-0.5 min-w-[4rem]", children: formatNumber(grandTotals.forecast) }) }) }), _jsx("td", { className: "px-3 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: _jsx("div", { className: "text-right", children: _jsx("span", { className: "inline-block px-1 py-0.5 min-w-[4rem]", children: formatNumber(grandTotals.actuals) }) }) }), _jsx("td", { className: `px-3 py-4 whitespace-nowrap text-right text-sm ${grandTotals.difference < 0
|
|
266
384
|
? "text-red-600 dark:text-red-400"
|
|
267
|
-
: "text-gray-900 dark:text-white"}`, children: formatNumber(grandTotals.difference) }), _jsx("td", { className: "px-3 py-4" }), _jsx("td", { className: "px-3 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white w-32", children: _jsx("div", { className: "text-right", children: _jsx("span", { className: "inline-block px-1 py-0.5 min-w-[4rem]", children: formatNumber(grandTotals.payments) }) }) })] })] })] }) })] }));
|
|
385
|
+
: "text-gray-900 dark:text-white"}`, children: formatNumber(grandTotals.difference) }), _jsx("td", { className: "px-3 py-4" }), _jsx("td", { className: "px-3 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white w-32", children: _jsx("div", { className: "text-right", children: _jsx("span", { className: "inline-block px-1 py-0.5 min-w-[4rem]", children: formatNumber(grandTotals.payments) }) }) }), _jsx("td", { className: "px-2 py-4" })] }), isAddingLineItem && (_jsx("tr", { ref: lineItemEditorRef, className: "bg-white dark:bg-gray-900 border-t border-gray-900 dark:border-gray-100", children: _jsx("td", { colSpan: 8, className: "px-6 py-4", children: _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-end gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsx("label", { className: "block text-xs font-medium text-gray-700 dark:text-gray-300 mb-2", children: "Select Category" }), _jsx(Select, { name: "category", searchable: true, value: selectedGroupId, onChange: (value) => handleCategoryChange(value), options: groupOptions, placeholder: "Choose a category...", className: "w-full" })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { onClick: handleSaveLineItem, disabled: !selectedGroupId || !!duplicateCategoryError, className: "bg-green-600 hover:bg-green-700 text-white", children: "SAVE" }), _jsx(Button, { onClick: handleCancelLineItem, variant: "secondary", children: "CANCEL" })] })] }), duplicateCategoryError && (_jsx("div", { className: "flex items-start gap-2 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-md", children: _jsx("span", { className: "text-sm text-amber-800 dark:text-amber-200", children: duplicateCategoryError }) }))] }) }) }))] })] }) }), deleteModalOpen && lineItemToDelete && (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [_jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-sm", onClick: handleCancelDelete }), _jsxs("div", { className: "relative bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full mx-4 overflow-hidden", children: [_jsx("div", { className: "px-6 py-4 border-b border-gray-200 dark:border-gray-700", children: _jsx("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white", children: "Delete Line Item" }) }), _jsx("div", { className: "px-6 py-4", children: _jsxs("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: ["Are you sure you want to delete the line item", " ", _jsxs("span", { className: "font-semibold text-gray-900 dark:text-white", children: ["\"", lineItemToDelete.label, "\""] }), "? This action cannot be undone."] }) }), _jsxs("div", { className: "px-6 py-4 bg-gray-50 dark:bg-gray-800/50 border-t border-gray-200 dark:border-gray-700 flex justify-end gap-3", children: [_jsx(Button, { onClick: handleCancelDelete, variant: "secondary", children: "CANCEL" }), _jsx(Button, { onClick: handleConfirmDelete, className: "bg-red-600 hover:bg-red-700 text-white", children: "DELETE" })] })] })] }))] }));
|
|
268
386
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpenseReportPDF.d.ts","sourceRoot":"","sources":["../../../../editors/expense-report/components/ExpenseReportPDF.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,MAAM,EACN,aAAa,EAEd,MAAM,sDAAsD,CAAC;AAE9D,UAAU,qBAAqB;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAmKD,wBAAgB,gBAAgB,CAAC,EAC/B,WAAW,EACX,SAAS,EACT,OAAO,EACP,MAAM,GACP,EAAE,qBAAqB,
|
|
1
|
+
{"version":3,"file":"ExpenseReportPDF.d.ts","sourceRoot":"","sources":["../../../../editors/expense-report/components/ExpenseReportPDF.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,MAAM,EACN,aAAa,EAEd,MAAM,sDAAsD,CAAC;AAE9D,UAAU,qBAAqB;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAmKD,wBAAgB,gBAAgB,CAAC,EAC/B,WAAW,EACX,SAAS,EACT,OAAO,EACP,MAAM,GACP,EAAE,qBAAqB,2CAqVvB"}
|
|
@@ -235,11 +235,11 @@ export function ExpenseReportPDF({ periodStart, periodEnd, wallets, groups, }) {
|
|
|
235
235
|
actuals: acc.actuals + (item.actuals || 0),
|
|
236
236
|
payments: acc.payments + (item.payments || 0),
|
|
237
237
|
}), { budget: 0, forecast: 0, actuals: 0, payments: 0 });
|
|
238
|
-
return (_jsxs(View, {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
238
|
+
return (_jsxs(View, { break: walletIndex > 0, children: [_jsxs(View, { wrap: false, children: [_jsxs(Text, { style: styles.sectionTitle, children: [periodStart &&
|
|
239
|
+
new Date(periodStart).toLocaleDateString("en-US", {
|
|
240
|
+
month: "short",
|
|
241
|
+
year: "numeric",
|
|
242
|
+
}), " ", "Breakdown"] }), _jsxs(Text, { style: styles.walletInfo, children: [wallet.name && `${wallet.name} • `, wallet.wallet || "Unknown Wallet"] })] }), _jsxs(View, { style: styles.table, children: [groupedItems.map((group, groupIndex) => {
|
|
243
243
|
const subtotals = group.items.reduce((acc, item) => ({
|
|
244
244
|
budget: acc.budget + (item.budget || 0),
|
|
245
245
|
forecast: acc.forecast + (item.forecast || 0),
|
|
@@ -247,13 +247,13 @@ export function ExpenseReportPDF({ periodStart, periodEnd, wallets, groups, }) {
|
|
|
247
247
|
payments: acc.payments + (item.payments || 0),
|
|
248
248
|
}), { budget: 0, forecast: 0, actuals: 0, payments: 0 });
|
|
249
249
|
const subtotalDifference = subtotals.forecast - subtotals.actuals;
|
|
250
|
-
return (_jsxs(View, { children: [_jsx(View, { style: {
|
|
250
|
+
return (_jsxs(View, { children: [groupIndex === 0 && (_jsxs(View, { style: styles.tableHeader, wrap: false, children: [_jsx(Text, { style: [styles.headerCell, styles.categoryCol], children: "Category" }), _jsx(Text, { style: [styles.headerCell, styles.budgetCol], children: "Budget" }), _jsx(Text, { style: [styles.headerCell, styles.forecastCol], children: "Forecast" }), _jsx(Text, { style: [styles.headerCell, styles.actualsCol], children: "Actuals" }), _jsx(Text, { style: [styles.headerCell, styles.differenceCol], children: "Difference" }), _jsx(Text, { style: [styles.headerCell, styles.commentsCol], children: "Comments" }), _jsx(Text, { style: [styles.headerCell, styles.paymentsCol], children: "Payments" })] })), _jsx(View, { style: {
|
|
251
251
|
paddingVertical: 6,
|
|
252
252
|
paddingHorizontal: 6,
|
|
253
253
|
backgroundColor: "#f9fafb",
|
|
254
254
|
borderBottom: "1pt solid #e5e7eb",
|
|
255
255
|
marginTop: groupIndex > 0 ? 8 : 0,
|
|
256
|
-
}, children: _jsx(Text, { style: {
|
|
256
|
+
}, wrap: false, children: _jsx(Text, { style: {
|
|
257
257
|
fontSize: 9,
|
|
258
258
|
fontWeight: "bold",
|
|
259
259
|
color: "#111827",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WalletsTable.d.ts","sourceRoot":"","sources":["../../../../editors/expense-report/components/WalletsTable.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,sDAAsD,CAAC;AAKlG,UAAU,iBAAiB;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,qBAAqB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,QAAQ,EAAE,GAAG,CAAC;CACf;AAED,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,MAAM,EACN,qBAAqB,EACrB,QAAQ,GACT,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"WalletsTable.d.ts","sourceRoot":"","sources":["../../../../editors/expense-report/components/WalletsTable.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,sDAAsD,CAAC;AAKlG,UAAU,iBAAiB;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,qBAAqB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,QAAQ,EAAE,GAAG,CAAC;CACf;AAED,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,MAAM,EACN,qBAAqB,EACrB,QAAQ,GACT,EAAE,iBAAiB,2CAwanB"}
|
|
@@ -122,7 +122,19 @@ export function WalletsTable({ wallets, groups, onAddBillingStatement, dispatch,
|
|
|
122
122
|
minimumFractionDigits: 2,
|
|
123
123
|
}).format(value);
|
|
124
124
|
};
|
|
125
|
-
return (_jsxs("div", { className: "space-y-4", children: [wallets.length > 0 ? (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "min-w-full divide-y divide-gray-200 dark:divide-gray-700", children: [_jsx("thead", { className: "bg-gray-50 dark:bg-gray-800", children: _jsxs("tr", { children: [_jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Wallet" }), _jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Monthly Budget" }), _jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Forecast" }), _jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children:
|
|
125
|
+
return (_jsxs("div", { className: "space-y-4", children: [wallets.length > 0 ? (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "min-w-full divide-y divide-gray-200 dark:divide-gray-700", children: [_jsx("thead", { className: "bg-gray-50 dark:bg-gray-800", children: _jsxs("tr", { children: [_jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Wallet" }), _jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Monthly Budget" }), _jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Forecast" }), _jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: _jsxs("div", { className: "flex items-center justify-end gap-2", children: [needsSync && (_jsx("button", { onClick: () => {
|
|
126
|
+
// Sync all outdated wallets
|
|
127
|
+
[...tagChangedWallets, ...outdatedWallets].forEach((walletAddress) => {
|
|
128
|
+
const wallet = wallets.find(w => w.wallet === walletAddress);
|
|
129
|
+
if (wallet) {
|
|
130
|
+
handleSyncWallet(wallet);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}, disabled: syncingWallet !== null, className: `inline-flex items-center justify-center w-8 h-8 rounded-md transition-colors ${tagChangedWallets.length > 0
|
|
134
|
+
? "text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20 hover:bg-red-100 dark:hover:bg-red-900/30 animate-pulse"
|
|
135
|
+
: "text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-900/20 hover:bg-amber-100 dark:hover:bg-amber-900/30 animate-pulse"} disabled:opacity-50 disabled:cursor-not-allowed`, title: tagChangedWallets.length > 0
|
|
136
|
+
? "ALERT: Tags have changed in billing statements - sync all wallets!"
|
|
137
|
+
: "Sync all wallets with latest billing statements", children: _jsx(RefreshCw, { size: 16, className: syncingWallet !== null ? "animate-spin" : "" }) })), _jsx("span", { children: "Actuals" })] }) }), _jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Difference" }), _jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Payments" }), _jsx("th", { className: "px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider", children: "Actions" })] }) }), _jsx("tbody", { className: "bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700", children: wallets.map((wallet) => {
|
|
126
138
|
const totals = calculateWalletTotals(wallet);
|
|
127
139
|
const isHovered = hoveredWallet === wallet.wallet;
|
|
128
140
|
return (_jsxs("tr", { onMouseEnter: () => setHoveredWallet(wallet.wallet || null), onMouseLeave: () => setHoveredWallet(null), className: "hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors", children: [_jsx("td", { className: "px-6 py-4 whitespace-nowrap", children: editingWallet === wallet.wallet ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(TextInput, { value: editingName, onChange: (e) => setEditingName(e.target.value), placeholder: "Enter wallet name", className: "flex-1", onKeyDown: (e) => {
|
|
@@ -132,23 +144,23 @@ export function WalletsTable({ wallets, groups, onAddBillingStatement, dispatch,
|
|
|
132
144
|
else if (e.key === "Escape") {
|
|
133
145
|
handleCancelEditName();
|
|
134
146
|
}
|
|
135
|
-
}, autoFocus: true }), _jsx("button", { onClick: () => handleSaveEditName(wallet.wallet || ""), className: "inline-flex items-center justify-center w-7 h-7 text-green-600 dark:text-green-400 hover:bg-green-50 dark:hover:bg-green-900/20 rounded-md transition-colors", title: "Save", children: _jsx(Check, { size: 14 }) }), _jsx("button", { onClick: handleCancelEditName, className: "inline-flex items-center justify-center w-7 h-7 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-900/20 rounded-md transition-colors", title: "Cancel", children: _jsx(X, { size: 14 }) })] })) : (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-white", children: wallet.name || "Unnamed Wallet" }), _jsx("button", { onClick: () => handleStartEditName(wallet), className: "inline-flex items-center justify-center w-6 h-6 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors", title: "Edit name", children: _jsx(Pencil, { size: 12 }) }), _jsxs("button", { onClick: () => handleCopyAddress(wallet.wallet || ""), className: "inline-flex items-center gap-1 px-2 py-1 text-xs text-gray-500 dark:text-gray-400 font-mono hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors", title: `Copy address: ${wallet.wallet}`, children: [formatAddress(wallet.wallet || ""), copiedWallet === wallet.wallet ? (_jsx(CheckCheck, { size: 12, className: "text-green-500" })) : (_jsx(Copy, { size: 12 }))] })] })) }), _jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: formatCurrency(totals.budget) }), _jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: formatCurrency(totals.forecast) }), _jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
147
|
+
}, autoFocus: true }), _jsx("button", { onClick: () => handleSaveEditName(wallet.wallet || ""), className: "inline-flex items-center justify-center w-7 h-7 text-green-600 dark:text-green-400 hover:bg-green-50 dark:hover:bg-green-900/20 rounded-md transition-colors", title: "Save", children: _jsx(Check, { size: 14 }) }), _jsx("button", { onClick: handleCancelEditName, className: "inline-flex items-center justify-center w-7 h-7 text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-900/20 rounded-md transition-colors", title: "Cancel", children: _jsx(X, { size: 14 }) })] })) : (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-white", children: wallet.name || "Unnamed Wallet" }), _jsx("button", { onClick: () => handleStartEditName(wallet), className: "inline-flex items-center justify-center w-6 h-6 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors", title: "Edit name", children: _jsx(Pencil, { size: 12 }) }), _jsxs("button", { onClick: () => handleCopyAddress(wallet.wallet || ""), className: "inline-flex items-center gap-1 px-2 py-1 text-xs text-gray-500 dark:text-gray-400 font-mono hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors", title: `Copy address: ${wallet.wallet}`, children: [formatAddress(wallet.wallet || ""), copiedWallet === wallet.wallet ? (_jsx(CheckCheck, { size: 12, className: "text-green-500" })) : (_jsx(Copy, { size: 12 }))] })] })) }), _jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: formatCurrency(totals.budget) }), _jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: formatCurrency(totals.forecast) }), _jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm", children: totals.actuals === 0 && (!wallet.billingStatements || wallet.billingStatements.length === 0) ? (
|
|
148
|
+
// When actuals is 0 and no billing statements, only show the Add Bills button
|
|
149
|
+
_jsx("div", { className: "flex items-center justify-end", children: _jsxs("button", { onClick: () => onAddBillingStatement(wallet.wallet || ""), className: "inline-flex items-center gap-1 px-3 py-1 text-sm font-medium text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 bg-blue-50 dark:bg-blue-900/20 hover:bg-blue-100 dark:hover:bg-blue-900/30 rounded-md transition-colors", title: "Add billing statement for this wallet", children: [_jsx(Plus, { size: 16 }), _jsx("span", { children: "Add Bills" })] }) })) : (
|
|
150
|
+
// When actuals is not 0 or has billing statements, show compact buttons + value horizontally
|
|
151
|
+
_jsxs("div", { className: "flex items-center justify-end gap-2", children: [_jsx("button", { onClick: () => onAddBillingStatement(wallet.wallet || ""), className: "inline-flex items-center justify-center w-8 h-8 text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 bg-blue-50 dark:bg-blue-900/20 hover:bg-blue-100 dark:hover:bg-blue-900/30 rounded-md transition-colors", title: "Add billing statement for this wallet", children: _jsx(Plus, { size: 16 }) }), wallet.billingStatements && wallet.billingStatements.length > 0 && (_jsx("button", { onClick: () => handleSyncWallet(wallet), disabled: syncingWallet === wallet.wallet, className: `inline-flex items-center justify-center w-8 h-8 rounded-md transition-colors ${tagChangedWallets.includes(wallet.wallet || "")
|
|
140
152
|
? "text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20 hover:bg-red-100 dark:hover:bg-red-900/30 animate-pulse"
|
|
141
153
|
: outdatedWallets.includes(wallet.wallet || "")
|
|
142
154
|
? "text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-900/20 hover:bg-amber-100 dark:hover:bg-amber-900/30 animate-pulse"
|
|
143
155
|
: "text-gray-600 dark:text-gray-400 bg-gray-50 dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700"} disabled:opacity-50 disabled:cursor-not-allowed`, title: tagChangedWallets.includes(wallet.wallet || "")
|
|
144
|
-
? "ALERT: Tags have changed
|
|
156
|
+
? "ALERT: Tags have changed - sync required!"
|
|
145
157
|
: outdatedWallets.includes(wallet.wallet || "")
|
|
146
|
-
? "Sync needed - billing statements
|
|
147
|
-
: "Sync with latest billing statements", children:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
158
|
+
? "Sync needed - billing statements updated"
|
|
159
|
+
: "Sync with latest billing statements", children: _jsx(RefreshCw, { size: 16, className: syncingWallet === wallet.wallet ? "animate-spin" : "" }) })), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-white", children: formatCurrency(totals.actuals) })] })) }), _jsx("td", { className: `px-6 py-4 whitespace-nowrap text-right text-sm font-medium ${totals.difference > 0
|
|
160
|
+
? "text-red-600 dark:text-red-400"
|
|
161
|
+
: totals.difference < 0
|
|
162
|
+
? "text-green-600 dark:text-green-400"
|
|
163
|
+
: "text-gray-900 dark:text-white"}`, children: formatCurrency(totals.difference) }), _jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm text-gray-900 dark:text-white", children: formatCurrency(totals.payments) }), _jsx("td", { className: "px-6 py-4 whitespace-nowrap text-right text-sm", children: _jsx("div", { className: "flex items-center justify-end gap-2", children: _jsx("button", { onClick: () => handleRemoveWallet(wallet.wallet || ""), className: "inline-flex items-center justify-center w-8 h-8 text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-md transition-colors", title: "Remove wallet", children: _jsx(Trash2, { size: 16 }) }) }) })] }, wallet.wallet));
|
|
152
164
|
}) })] }) })) : (_jsx("div", { className: "text-center py-12 text-gray-500 dark:text-gray-400", children: _jsx("p", { className: "text-sm", children: "No wallets added yet. Add a wallet to get started." }) })), _jsxs("div", { className: "flex items-end gap-3 pt-4 border-t border-gray-200 dark:border-gray-700", children: [_jsxs("div", { className: "flex-1", children: [_jsx("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1", children: "Wallet Name" }), _jsx(TextInput, { value: newWalletName, onChange: (e) => setNewWalletName(e.target.value), placeholder: "Enter wallet name (optional)", onKeyDown: (e) => {
|
|
153
165
|
if (e.key === "Enter") {
|
|
154
166
|
handleAddWallet();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bankSection.d.ts","sourceRoot":"","sources":["../../../../editors/invoice/legalEntity/bankSection.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EAOtB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAMtE,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAC5C,qBAAqB,CAAC,KAAK,CAAC,EAC5B,UAAU,CACX,GAAG;IACF,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,CAAC;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACrD,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,aAAa,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAmCF,eAAO,MAAM,sBAAsB,
|
|
1
|
+
{"version":3,"file":"bankSection.d.ts","sourceRoot":"","sources":["../../../../editors/invoice/legalEntity/bankSection.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EAOtB,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAG5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAMtE,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAC5C,qBAAqB,CAAC,KAAK,CAAC,EAC5B,UAAU,CACX,GAAG;IACF,QAAQ,CAAC,KAAK,EAAE,wBAAwB,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,CAAC;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACrD,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClD,QAAQ,CAAC,aAAa,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACtD,QAAQ,CAAC,uBAAuB,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAmCF,eAAO,MAAM,sBAAsB,qIAoiBlC,CAAC"}
|
|
@@ -79,24 +79,24 @@ export const LegalEntityBankSection = forwardRef(function LegalEntityBankSection
|
|
|
79
79
|
}
|
|
80
80
|
const SEPA_SWIFT_CURRENCIES = ["EUR", "DKK", "GBP", "CHF", "JPY"];
|
|
81
81
|
const usdIbanPayment = useMemo(() => isValidIBAN(localState.accountNum ?? "") && currency === "USD", [localState.accountNum, currency]);
|
|
82
|
-
return (_jsxs("div", { ...divProps, className: twMerge("rounded-lg border border-gray-200 bg-white p-6", props.className), ref: ref, children: [_jsx("h3", { className: "mb-4 text-lg font-semibold text-gray-900", children: "Banking Information" }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "space-y-2", children: _jsx(InputField
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
82
|
+
return (_jsxs("div", { ...divProps, className: twMerge("rounded-lg border border-gray-200 bg-white p-6", props.className), ref: ref, children: [_jsx("h3", { className: "mb-4 text-lg font-semibold text-gray-900", children: "Banking Information" }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "space-y-2", children: _jsxs("div", { children: [_jsxs("label", { className: "block text-sm font-medium text-gray-700 mb-1", children: ["Account Number", isValidIBAN(localState.accountNum ?? "") && (_jsx("span", { className: "ml-2 text-green-600 font-medium", children: "IBAN" }))] }), _jsx(InputField
|
|
83
|
+
// input={localState.accountNum ?? ""}
|
|
84
|
+
, {
|
|
85
|
+
// input={localState.accountNum ?? ""}
|
|
86
|
+
value: localState.accountNum ?? "", placeholder: "Account Number", onBlur: createBlurHandler("accountNum"), handleInputChange: createInputHandler("accountNum"), className: "h-10 w-full text-md mb-2", validation:
|
|
87
|
+
// Prefer the first failing validation between IBAN and generic account number
|
|
88
|
+
(() => {
|
|
89
|
+
const firstInvalid = (ibanvalidation &&
|
|
90
|
+
!ibanvalidation.isValid &&
|
|
91
|
+
ibanvalidation) ||
|
|
92
|
+
(accountNumbervalidation &&
|
|
93
|
+
!accountNumbervalidation.isValid &&
|
|
94
|
+
accountNumbervalidation);
|
|
95
|
+
return (firstInvalid ||
|
|
96
|
+
ibanvalidation ||
|
|
97
|
+
accountNumbervalidation ||
|
|
98
|
+
null);
|
|
99
|
+
})() })] }) }), _jsx("div", { className: "space-y-2", children: _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx("div", { className: "space-y-2", children: _jsx(Select, { className: "h-10 w-full text-md mb-2", label: "Account Type", options: ACCOUNT_TYPES.map((type) => ({
|
|
100
100
|
label: type,
|
|
101
101
|
value: type,
|
|
102
102
|
})), value: localState.accountType ?? "", onChange: (value) => {
|
|
@@ -49,7 +49,7 @@ export function LineItemTagsTable({ lineItems, onClose, dispatch, paymentAccount
|
|
|
49
49
|
id: item.id,
|
|
50
50
|
description: e.target.value,
|
|
51
51
|
}));
|
|
52
|
-
} }) }), _jsx("td", { className: "border-b border-gray-200 w-48", children: _jsx(DatePicker, { name: "period", dateFormat: "YYYY-MM", autoClose: true, placeholder: "Select Period", value: item.lineItemTag.find((tag) => tag.dimension === "accounting-period")?.label || "", onChange: (e) => dispatch(actions.setLineItemTag({
|
|
52
|
+
} }) }), _jsx("td", { className: "border-b border-gray-200 w-48", children: _jsx(DatePicker, { name: "period", dateFormat: "YYYY-MM-DD", autoClose: true, placeholder: "Select Period", value: item.lineItemTag.find((tag) => tag.dimension === "accounting-period")?.label || "", onChange: (e) => dispatch(actions.setLineItemTag({
|
|
53
53
|
lineItemId: item.id,
|
|
54
54
|
dimension: "accounting-period",
|
|
55
55
|
value: new Date(e.target.value)
|
package/dist/style.css
CHANGED
|
@@ -355,6 +355,9 @@
|
|
|
355
355
|
max-width: 96rem;
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
|
+
.mx-4 {
|
|
359
|
+
margin-inline: calc(var(--spacing) * 4);
|
|
360
|
+
}
|
|
358
361
|
.mx-auto {
|
|
359
362
|
margin-inline: auto;
|
|
360
363
|
}
|
|
@@ -825,6 +828,9 @@
|
|
|
825
828
|
--tw-border-style: dashed;
|
|
826
829
|
border-style: dashed;
|
|
827
830
|
}
|
|
831
|
+
.border-amber-200 {
|
|
832
|
+
border-color: var(--color-amber-200);
|
|
833
|
+
}
|
|
828
834
|
.border-blue-100 {
|
|
829
835
|
border-color: var(--color-blue-100);
|
|
830
836
|
}
|
|
@@ -846,6 +852,9 @@
|
|
|
846
852
|
.border-gray-500 {
|
|
847
853
|
border-color: var(--color-gray-500);
|
|
848
854
|
}
|
|
855
|
+
.border-gray-900 {
|
|
856
|
+
border-color: var(--color-gray-900);
|
|
857
|
+
}
|
|
849
858
|
.border-green-500 {
|
|
850
859
|
border-color: var(--color-green-500);
|
|
851
860
|
}
|
|
@@ -876,6 +885,12 @@
|
|
|
876
885
|
background-color: color-mix(in oklab, var(--color-black) 30%, transparent);
|
|
877
886
|
}
|
|
878
887
|
}
|
|
888
|
+
.bg-black\/50 {
|
|
889
|
+
background-color: color-mix(in srgb, #000 50%, transparent);
|
|
890
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
891
|
+
background-color: color-mix(in oklab, var(--color-black) 50%, transparent);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
879
894
|
.bg-blue-50 {
|
|
880
895
|
background-color: var(--color-blue-50);
|
|
881
896
|
}
|
|
@@ -939,6 +954,9 @@
|
|
|
939
954
|
.p-0 {
|
|
940
955
|
padding: calc(var(--spacing) * 0);
|
|
941
956
|
}
|
|
957
|
+
.p-0\.5 {
|
|
958
|
+
padding: calc(var(--spacing) * 0.5);
|
|
959
|
+
}
|
|
942
960
|
.p-1 {
|
|
943
961
|
padding: calc(var(--spacing) * 1);
|
|
944
962
|
}
|
|
@@ -1011,9 +1029,15 @@
|
|
|
1011
1029
|
.pt-6 {
|
|
1012
1030
|
padding-top: calc(var(--spacing) * 6);
|
|
1013
1031
|
}
|
|
1032
|
+
.pr-3 {
|
|
1033
|
+
padding-right: calc(var(--spacing) * 3);
|
|
1034
|
+
}
|
|
1014
1035
|
.pb-2 {
|
|
1015
1036
|
padding-bottom: calc(var(--spacing) * 2);
|
|
1016
1037
|
}
|
|
1038
|
+
.pl-6 {
|
|
1039
|
+
padding-left: calc(var(--spacing) * 6);
|
|
1040
|
+
}
|
|
1017
1041
|
.pl-11 {
|
|
1018
1042
|
padding-left: calc(var(--spacing) * 11);
|
|
1019
1043
|
}
|
|
@@ -1088,6 +1112,9 @@
|
|
|
1088
1112
|
.text-amber-600 {
|
|
1089
1113
|
color: var(--color-amber-600);
|
|
1090
1114
|
}
|
|
1115
|
+
.text-amber-800 {
|
|
1116
|
+
color: var(--color-amber-800);
|
|
1117
|
+
}
|
|
1091
1118
|
.text-black {
|
|
1092
1119
|
color: var(--color-black);
|
|
1093
1120
|
}
|
|
@@ -1432,6 +1459,13 @@
|
|
|
1432
1459
|
}
|
|
1433
1460
|
}
|
|
1434
1461
|
}
|
|
1462
|
+
.hover\:text-red-800 {
|
|
1463
|
+
&:hover {
|
|
1464
|
+
@media (hover: hover) {
|
|
1465
|
+
color: var(--color-red-800);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1435
1469
|
.hover\:opacity-80 {
|
|
1436
1470
|
&:hover {
|
|
1437
1471
|
@media (hover: hover) {
|
|
@@ -1603,6 +1637,16 @@
|
|
|
1603
1637
|
}
|
|
1604
1638
|
}
|
|
1605
1639
|
}
|
|
1640
|
+
.dark\:border-amber-800 {
|
|
1641
|
+
&:where(.dark, .dark *) {
|
|
1642
|
+
border-color: var(--color-amber-800);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
.dark\:border-gray-100 {
|
|
1646
|
+
&:where(.dark, .dark *) {
|
|
1647
|
+
border-color: var(--color-gray-100);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1606
1650
|
.dark\:border-gray-500 {
|
|
1607
1651
|
&:where(.dark, .dark *) {
|
|
1608
1652
|
border-color: var(--color-gray-500);
|
|
@@ -1670,6 +1714,11 @@
|
|
|
1670
1714
|
}
|
|
1671
1715
|
}
|
|
1672
1716
|
}
|
|
1717
|
+
.dark\:text-amber-200 {
|
|
1718
|
+
&:where(.dark, .dark *) {
|
|
1719
|
+
color: var(--color-amber-200);
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1673
1722
|
.dark\:text-amber-400 {
|
|
1674
1723
|
&:where(.dark, .dark *) {
|
|
1675
1724
|
color: var(--color-amber-400);
|
|
@@ -1850,6 +1899,15 @@
|
|
|
1850
1899
|
}
|
|
1851
1900
|
}
|
|
1852
1901
|
}
|
|
1902
|
+
.dark\:hover\:text-red-300 {
|
|
1903
|
+
&:where(.dark, .dark *) {
|
|
1904
|
+
&:hover {
|
|
1905
|
+
@media (hover: hover) {
|
|
1906
|
+
color: var(--color-red-300);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1853
1911
|
.dark\:focus\:ring-blue-400 {
|
|
1854
1912
|
&:where(.dark, .dark *) {
|
|
1855
1913
|
&:focus {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerhousedao/contributor-billing",
|
|
3
3
|
"description": "Document models that help contributors of open organisations get paid anonymously for their work on a monthly basis.",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.6",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -93,6 +93,7 @@
|
|
|
93
93
|
"@powerhousedao/reactor-local": "^5.0.0-staging.30",
|
|
94
94
|
"@powerhousedao/scalars": "^1.33.1-staging.5",
|
|
95
95
|
"@powerhousedao/switchboard": "^5.0.0-staging.30",
|
|
96
|
+
"@powerhousedao/vetra": "5.0.0-staging.30",
|
|
96
97
|
"@tailwindcss/cli": "^4.1.4",
|
|
97
98
|
"@testing-library/react": "^16.3.0",
|
|
98
99
|
"@types/node": "^22.14.1",
|