@powerhousedao/contributor-billing 0.0.79 → 0.0.80
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/document-models/integrations/gen/ph-factories.js +6 -6
- package/dist/document-models/integrations/gen/utils.d.ts.map +1 -1
- package/dist/document-models/integrations/gen/utils.js +6 -6
- package/dist/editors/contributor-billing/components/DriveExplorer.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/DriveExplorer.js +8 -2
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.js +5 -5
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.d.ts +2 -0
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.d.ts.map +1 -0
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.js +68 -0
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts +3 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.js +2 -7
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.js +10 -1
- package/dist/editors/contributor-billing/util.d.ts +9 -0
- package/dist/editors/contributor-billing/util.d.ts.map +1 -0
- package/dist/editors/contributor-billing/util.js +81 -0
- package/dist/style.css +78 -66
- package/package.json +12 -12
|
@@ -11,16 +11,16 @@ export function defaultGlobalState() {
|
|
|
11
11
|
},
|
|
12
12
|
googleCloud: {
|
|
13
13
|
keyFile: {
|
|
14
|
-
type: "",
|
|
15
14
|
project_id: "",
|
|
16
|
-
private_key_id: "",
|
|
17
|
-
private_key: "",
|
|
18
15
|
client_email: "",
|
|
19
|
-
|
|
20
|
-
auth_uri: "",
|
|
21
|
-
token_uri: "",
|
|
16
|
+
private_key: "",
|
|
22
17
|
auth_provider_x509_cert_url: "",
|
|
18
|
+
auth_uri: "",
|
|
19
|
+
client_id: "",
|
|
23
20
|
client_x509_cert_url: "",
|
|
21
|
+
private_key_id: "",
|
|
22
|
+
token_uri: "",
|
|
23
|
+
type: "",
|
|
24
24
|
universe_domain: "",
|
|
25
25
|
},
|
|
26
26
|
location: "",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../document-models/integrations/gen/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,aAAa,EAQnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,eAAO,MAAM,kBAAkB,EAAE,iBA2BhC,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../document-models/integrations/gen/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,aAAa,EAQnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,eAAO,MAAM,kBAAkB,EAAE,iBA2BhC,CAAC;AACF,eAAO,MAAM,iBAAiB,EAAE,sBAA2B,CAAC;AAE5D,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,mBAAmB,CAMxD,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,mBAAmB,CAM9D,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,UAAU,GAAG,EAAE,MAAM,MAAM,EAAE,OAAO,MAAM,oBAEpE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,UAAU,GAAG,EAAE,OAAO,GAAG,kBAEzD,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,YAAY,CAAC,mBAAmB,CAE1D,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,mBAAmB,CAE5D,CAAC;AAEF,QAAA,MAAM,KAAK;;;;2BAhB0B,GAAG,QAAQ,MAAM,SAAS,MAAM;iCAI1B,GAAG,SAAS,GAAG;;;CAoBzD,CAAC;AAEF,eAAe,KAAK,CAAC"}
|
|
@@ -7,16 +7,16 @@ export const initialGlobalState = {
|
|
|
7
7
|
},
|
|
8
8
|
googleCloud: {
|
|
9
9
|
keyFile: {
|
|
10
|
-
type: "",
|
|
11
10
|
project_id: "",
|
|
12
|
-
private_key_id: "",
|
|
13
|
-
private_key: "",
|
|
14
11
|
client_email: "",
|
|
15
|
-
|
|
16
|
-
auth_uri: "",
|
|
17
|
-
token_uri: "",
|
|
12
|
+
private_key: "",
|
|
18
13
|
auth_provider_x509_cert_url: "",
|
|
14
|
+
auth_uri: "",
|
|
15
|
+
client_id: "",
|
|
19
16
|
client_x509_cert_url: "",
|
|
17
|
+
private_key_id: "",
|
|
18
|
+
token_uri: "",
|
|
19
|
+
type: "",
|
|
20
20
|
universe_domain: "",
|
|
21
21
|
},
|
|
22
22
|
location: "",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/DriveExplorer.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,gBAAgB,EAUtB,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/DriveExplorer.tsx"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,gBAAgB,EAUtB,MAAM,gCAAgC,CAAC;AAUxC;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,gBAAgB,2CAsUpD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { CreateDocumentModal, useDrop } from "@powerhousedao/design-system";
|
|
3
3
|
import { addDocument, useDocumentModelModules, useDriveContext, useDriveSharingType, useEditorModules, useFileChildNodes, useSelectedDrive, useSelectedDriveDocuments, useSelectedFolder, useSelectedNodePath, } from "@powerhousedao/reactor-browser";
|
|
4
4
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
@@ -6,12 +6,18 @@ import { EditorContainer } from "./EditorContainer.js";
|
|
|
6
6
|
import { InvoiceTable } from "./InvoiceTable/InvoiceTable.js";
|
|
7
7
|
import { twMerge } from "tailwind-merge";
|
|
8
8
|
import { ToastContainer } from "@powerhousedao/design-system";
|
|
9
|
+
import { HeaderStats } from "./InvoiceTable/HeaderStats.js";
|
|
9
10
|
/**
|
|
10
11
|
* Main drive explorer component with sidebar navigation and content area.
|
|
11
12
|
* Layout: Left sidebar (folder tree) + Right content area (files/folders + document editor)
|
|
12
13
|
*/
|
|
13
14
|
export function DriveExplorer(props) {
|
|
14
15
|
const [selected, setSelected] = useState({});
|
|
16
|
+
const [selectedStatuses, setSelectedStatuses] = useState([]);
|
|
17
|
+
// Handler for status filter changes
|
|
18
|
+
const handleStatusChange = useCallback((value) => {
|
|
19
|
+
setSelectedStatuses(Array.isArray(value) ? value : [value]);
|
|
20
|
+
}, []);
|
|
15
21
|
// === DOCUMENT EDITOR STATE ===
|
|
16
22
|
// Customize document opening/closing behavior here
|
|
17
23
|
const [activeDocumentId, setActiveDocumentId] = useState();
|
|
@@ -197,5 +203,5 @@ export function DriveExplorer(props) {
|
|
|
197
203
|
// console.log("dropProps", dropProps);
|
|
198
204
|
return (_jsx("div", { className: "flex h-full editor-container", children: _jsxs("div", { ...dropProps, className: twMerge("rounded-md border-2 border-transparent ", isDropTarget && "border-dashed border-blue-100"), children: [_jsx(ToastContainer, { position: "bottom-right", autoClose: 5000, hideProgressBar: false, newestOnTop: false, closeOnClick: false, rtl: false, pauseOnFocusLoss: true, draggable: true, pauseOnHover: true, theme: "light" }), _jsx("div", { className: "flex-1 p-4", children: activeDocument && documentModelModule && editorModule ? (
|
|
199
205
|
// Document editor view
|
|
200
|
-
_jsx(EditorContainer, { handleClose: () => setActiveDocumentId(undefined), activeDocumentId: activeDocumentId || "" })) : (_jsx(InvoiceTable, { setActiveDocumentId: setActiveDocumentId, files: fileChildren, state: state || [], selected: selected, setSelected: setSelected, onBatchAction: () => { }, onDeleteNode: () => { }, renameNode: () => { }, filteredDocumentModels: documentModelModules, onSelectDocumentModel: onSelectDocumentModel, getDocDispatcher: getDocDispatcher })) }), _jsx(CreateDocumentModal, { onContinue: onCreateDocument, onOpenChange: (open) => setOpenModal(open), open: openModal })] }) }));
|
|
206
|
+
_jsx(EditorContainer, { handleClose: () => setActiveDocumentId(undefined), activeDocumentId: activeDocumentId || "" })) : (_jsxs(_Fragment, { children: [_jsx(HeaderStats, {}), _jsx(InvoiceTable, { setActiveDocumentId: setActiveDocumentId, files: fileChildren, state: state || [], selected: selected, setSelected: setSelected, onBatchAction: () => { }, onDeleteNode: () => { }, renameNode: () => { }, filteredDocumentModels: documentModelModules, onSelectDocumentModel: onSelectDocumentModel, getDocDispatcher: getDocDispatcher, selectedStatuses: selectedStatuses, onStatusChange: handleStatusChange })] })) }), _jsx(CreateDocumentModal, { onContinue: onCreateDocument, onOpenChange: (open) => setOpenModal(open), open: openModal })] }) }));
|
|
201
207
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeaderControls.d.ts","sourceRoot":"","sources":["../../../../../editors/contributor-billing/components/InvoiceTable/HeaderControls.tsx"],"names":[],"mappings":"AAcA,eAAO,MAAM,cAAc,GAAI,yMAY5B;IACD,kBAAkB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,aAAa,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACnD,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IACzD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IACpD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,0BAA0B,CAAC,EAAE,MAAM,IAAI,CAAC;IACxC,eAAe,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAC7B,mBAAmB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C,
|
|
1
|
+
{"version":3,"file":"HeaderControls.d.ts","sourceRoot":"","sources":["../../../../../editors/contributor-billing/components/InvoiceTable/HeaderControls.tsx"],"names":[],"mappings":"AAcA,eAAO,MAAM,cAAc,GAAI,yMAY5B;IACD,kBAAkB,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,aAAa,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACnD,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IACzD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IACpD,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,0BAA0B,CAAC,EAAE,MAAM,IAAI,CAAC;IACxC,eAAe,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAC7B,mBAAmB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C,4CAiKA,CAAC"}
|
|
@@ -29,7 +29,10 @@ export const HeaderControls = ({ contributorOptions = [], statusOptions = [], on
|
|
|
29
29
|
selectedStatuses.every((status) => allowedStatuses.includes(status));
|
|
30
30
|
const [showCurrencyModal, setShowCurrencyModal] = useState(false);
|
|
31
31
|
const [selectedCurrency, setSelectedCurrency] = useState("CHF");
|
|
32
|
-
return (_jsxs("div", { className: "flex flex-col gap-4 mb-4", children: [_jsxs("div", { className: "flex justify-between items-center", children: [
|
|
32
|
+
return (_jsxs("div", { className: "flex flex-col gap-4 mb-4", children: [_jsxs("div", { className: "flex justify-between items-center", children: [_jsxs("div", { className: "flex gap-2 items-center", children: [_jsx(Select, { style: {
|
|
33
|
+
width: "200px",
|
|
34
|
+
height: "30px",
|
|
35
|
+
}, options: statusOptions, onChange: onStatusChange, placeholder: "Status", selectionIcon: "checkmark", multiple: true, value: selectedStatuses }), _jsx("input", { type: "text", className: "border rounded px-2 py-1 text-sm", placeholder: "Search", onChange: (e) => onSearchChange?.(e.target.value) })] }), _jsxs("div", { className: "flex gap-2 items-center", children: [_jsx("button", { className: `bg-white border border-gray-300 rounded px-3 py-1 text-sm hover:bg-gray-100 ${!canExport ? "opacity-50 cursor-not-allowed" : ""}`, onClick: () => setShowCurrencyModal(true), disabled: !canExport, children: "Export to CSV" }), _jsx(Select, { style: {
|
|
33
36
|
width: "180px",
|
|
34
37
|
height: "30px",
|
|
35
38
|
}, options: batchOptions, onChange: (value) => onBatchAction?.(value), placeholder: "Batch Action", selectionIcon: "checkmark" }), _jsx("div", { className: "cursor-pointer", children: _jsx(Icon, { name: "Settings", className: "hover:text-blue-300", onClick: () => {
|
|
@@ -40,10 +43,7 @@ export const HeaderControls = ({ contributorOptions = [], statusOptions = [], on
|
|
|
40
43
|
else {
|
|
41
44
|
setActiveDocumentId?.(integrationsDoc.id);
|
|
42
45
|
}
|
|
43
|
-
} }) })] })] }), _jsxs("div", { className: "
|
|
44
|
-
width: "200px",
|
|
45
|
-
height: "30px",
|
|
46
|
-
}, options: statusOptions, onChange: onStatusChange, placeholder: "Status", selectionIcon: "checkmark", multiple: true }), _jsx("input", { type: "text", className: "border rounded px-2 py-1 text-sm", placeholder: "Search", onChange: (e) => onSearchChange?.(e.target.value) })] }), showCurrencyModal && (_jsx("div", { className: "fixed inset-0", children: _jsxs("div", { className: "absolute left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2 bg-white rounded shadow-lg p-6 min-w-[300px] flex flex-col items-center", children: [_jsx("h3", { className: "text-lg font-semibold mb-4", children: "Select Base Currency" }), _jsx("p", { className: "text-red-600 text-sm mb-2", children: "Chosen currency should match the base currency of the accounting." }), _jsx("select", { className: "border border-gray-300 rounded px-2 py-1 mb-4", value: selectedCurrency, onChange: (e) => setSelectedCurrency(e.target.value), children: currencyOptions.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) }), _jsxs("div", { className: "flex gap-2", children: [_jsx("button", { className: "bg-blue-600 text-white px-4 py-1 rounded hover:bg-blue-700", onClick: () => {
|
|
46
|
+
} }) })] })] }), showCurrencyModal && (_jsx("div", { className: "fixed inset-0", children: _jsxs("div", { className: "absolute left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2 bg-white rounded shadow-lg p-6 min-w-[300px] flex flex-col items-center", children: [_jsx("h3", { className: "text-lg font-semibold mb-4", children: "Select Base Currency" }), _jsx("p", { className: "text-red-600 text-sm mb-2", children: "Chosen currency should match the base currency of the accounting." }), _jsx("select", { className: "border border-gray-300 rounded px-2 py-1 mb-4", value: selectedCurrency, onChange: (e) => setSelectedCurrency(e.target.value), children: currencyOptions.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) }), _jsxs("div", { className: "flex gap-2", children: [_jsx("button", { className: "bg-blue-600 text-white px-4 py-1 rounded hover:bg-blue-700", onClick: () => {
|
|
47
47
|
setShowCurrencyModal(false);
|
|
48
48
|
onExport?.(selectedCurrency);
|
|
49
49
|
}, children: "Export" }), _jsx("button", { className: "bg-gray-200 px-4 py-1 rounded hover:bg-gray-300", onClick: () => setShowCurrencyModal(false), children: "Cancel" })] })] }) })), _jsxs(ConfirmationModal, { open: showCurrencyModal, onCancel: () => setShowCurrencyModal(false), onContinue: () => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeaderStats.d.ts","sourceRoot":"","sources":["../../../../../editors/contributor-billing/components/InvoiceTable/HeaderStats.tsx"],"names":[],"mappings":"AAkBA,eAAO,MAAM,WAAW,+CA6GvB,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Select } from "@powerhousedao/document-engineering/ui";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { useSelectedDriveDocuments } from "@powerhousedao/reactor-browser";
|
|
5
|
+
import { getExchangeRate } from "../../util.js";
|
|
6
|
+
import { Tooltip, TooltipProvider } from "@powerhousedao/design-system";
|
|
7
|
+
const currencyList = [
|
|
8
|
+
{ ticker: "USDS", crypto: true },
|
|
9
|
+
{ ticker: "DAI", crypto: true },
|
|
10
|
+
{ ticker: "USD", crypto: false },
|
|
11
|
+
{ ticker: "EUR", crypto: false },
|
|
12
|
+
{ ticker: "DKK", crypto: false },
|
|
13
|
+
{ ticker: "GBP", crypto: false },
|
|
14
|
+
{ ticker: "JPY", crypto: false },
|
|
15
|
+
{ ticker: "CNY", crypto: false },
|
|
16
|
+
{ ticker: "CHF", crypto: false },
|
|
17
|
+
];
|
|
18
|
+
export const HeaderStats = () => {
|
|
19
|
+
const [selectedCurrency, setSelectedCurrency] = useState("USD");
|
|
20
|
+
const [totalExpenses, setTotalExpenses] = useState(0);
|
|
21
|
+
const documents = useSelectedDriveDocuments();
|
|
22
|
+
const invoices = documents?.filter((doc) => doc.header.documentType === "powerhouse/invoice");
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const calculateTotalExpenses = async () => {
|
|
25
|
+
if (!invoices || invoices.length === 0) {
|
|
26
|
+
setTotalExpenses(0);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let total = 0;
|
|
30
|
+
for (const doc of invoices) {
|
|
31
|
+
const invoice = doc;
|
|
32
|
+
const invoiceAmount = invoice.state.global.totalPriceTaxIncl;
|
|
33
|
+
let invoiceCurrency = invoice.state.global.currency;
|
|
34
|
+
let selectCurrency = selectedCurrency;
|
|
35
|
+
if (invoiceCurrency === selectedCurrency) {
|
|
36
|
+
total += invoiceAmount;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
try {
|
|
40
|
+
if (invoiceCurrency === "DAI" || invoiceCurrency === "USDS") {
|
|
41
|
+
invoiceCurrency = "USD";
|
|
42
|
+
}
|
|
43
|
+
if (selectedCurrency === "DAI" || selectedCurrency === "USDS") {
|
|
44
|
+
selectCurrency = "USD";
|
|
45
|
+
}
|
|
46
|
+
const exchangeRate = await getExchangeRate(invoiceCurrency, selectCurrency);
|
|
47
|
+
total += invoiceAmount * exchangeRate;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error("Error getting exchange rate:", error);
|
|
51
|
+
// Fallback to original amount if exchange rate fails
|
|
52
|
+
total += invoiceAmount;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
setTotalExpenses(total);
|
|
57
|
+
};
|
|
58
|
+
calculateTotalExpenses();
|
|
59
|
+
}, [invoices, selectedCurrency]);
|
|
60
|
+
const currencyOptions = currencyList.map((currency) => ({
|
|
61
|
+
label: currency.ticker,
|
|
62
|
+
value: currency.ticker,
|
|
63
|
+
}));
|
|
64
|
+
return (_jsx("div", { className: "bg-white rounded-lg border border-gray-200 p-4 shadow-sm", children: _jsxs(TooltipProvider, { delayDuration: 0, skipDelayDuration: 0, children: [_jsxs("div", { className: "flex justify-between items-center mb-4", children: [_jsx("h1", { className: "text-xl font-semibold text-gray-900", children: "Operational Hub" }), _jsx("div", { className: "max-w-[200px]", children: _jsx(Select, { style: { width: "200px" }, options: currencyOptions, value: selectedCurrency, onChange: (value) => setSelectedCurrency(value), placeholder: "Select Currency" }) })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "bg-gray-50 rounded-lg p-3", children: [_jsxs("div", { className: "flex items-center gap-1 mb-1", children: [_jsx("h3", { className: "text-sm font-medium text-gray-600", children: "Total Expenses" }), _jsx(Tooltip, { content: "Approximate value calculated using exchangerate-api.com. DAI + USDS are converted to USD for simplicity", side: "right", children: _jsx("div", { className: "w-4 h-4 rounded-full bg-gray-300 text-white text-xs flex items-center justify-center cursor-help", children: "!" }) })] }), _jsxs("p", { className: "text-xl font-bold text-gray-900", children: [selectedCurrency, " ", totalExpenses.toLocaleString("en-US", {
|
|
65
|
+
minimumFractionDigits: 2,
|
|
66
|
+
maximumFractionDigits: 2,
|
|
67
|
+
})] })] }), _jsxs("div", { className: "bg-gray-50 rounded-lg p-3", children: [_jsx("h3", { className: "text-sm font-medium text-gray-600 mb-1", children: "Total Invoices" }), _jsx("p", { className: "text-xl font-bold text-gray-900", children: invoices?.length })] })] })] }) }));
|
|
68
|
+
};
|
|
@@ -19,7 +19,9 @@ interface InvoiceTableProps {
|
|
|
19
19
|
filteredDocumentModels: DocumentModelModule[];
|
|
20
20
|
onSelectDocumentModel: (model: DocumentModelModule) => void;
|
|
21
21
|
getDocDispatcher: (id: string) => any;
|
|
22
|
+
selectedStatuses: string[];
|
|
23
|
+
onStatusChange: (value: string | string[]) => void;
|
|
22
24
|
}
|
|
23
|
-
export declare const InvoiceTable: ({ files, state, setActiveDocumentId, selected, setSelected, onBatchAction, onDeleteNode, renameNode, filteredDocumentModels, onSelectDocumentModel, getDocDispatcher, }: InvoiceTableProps) => import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export declare const InvoiceTable: ({ files, state, setActiveDocumentId, selected, setSelected, onBatchAction, onDeleteNode, renameNode, filteredDocumentModels, onSelectDocumentModel, getDocDispatcher, selectedStatuses, onStatusChange, }: InvoiceTableProps) => import("react/jsx-runtime").JSX.Element;
|
|
24
26
|
export {};
|
|
25
27
|
//# sourceMappingURL=InvoiceTable.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvoiceTable.d.ts","sourceRoot":"","sources":["../../../../../editors/contributor-billing/components/InvoiceTable/InvoiceTable.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAsB1D,UAAU,iBAAiB;IACzB,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;IAC7B,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,QAAQ,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACpC,WAAW,EAAE,CACX,QAAQ,EACJ;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GACzB,CAAC,CAAC,IAAI,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,KAAK;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,KACjE,IAAI,CAAC;IACV,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,sBAAsB,EAAE,mBAAmB,EAAE,CAAC;IAC9C,qBAAqB,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC5D,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"InvoiceTable.d.ts","sourceRoot":"","sources":["../../../../../editors/contributor-billing/components/InvoiceTable/InvoiceTable.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAsB1D,UAAU,iBAAiB;IACzB,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;IAC7B,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,QAAQ,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IACpC,WAAW,EAAE,CACX,QAAQ,EACJ;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GACzB,CAAC,CAAC,IAAI,EAAE;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,KAAK;QAAE,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,KACjE,IAAI,CAAC;IACV,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,sBAAsB,EAAE,mBAAmB,EAAE,CAAC;IAC9C,qBAAqB,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC5D,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,GAAG,CAAC;IACtC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;CACpD;AAED,eAAO,MAAM,YAAY,GAAI,2MAc1B,iBAAiB,4CAyqBnB,CAAC"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import { useState } from "react";
|
|
4
3
|
import { HeaderControls } from "./HeaderControls.js";
|
|
5
4
|
import { InvoiceTableSection } from "./InvoiceTableSection.js";
|
|
6
5
|
import { InvoiceTableRow } from "./InvoiceTableRow.js";
|
|
@@ -21,8 +20,7 @@ const statusOptions = [
|
|
|
21
20
|
{ label: "Rejected", value: "REJECTED" },
|
|
22
21
|
{ label: "Other", value: "OTHER" },
|
|
23
22
|
];
|
|
24
|
-
export const InvoiceTable = ({ files, state, setActiveDocumentId, selected, setSelected, onBatchAction, onDeleteNode, renameNode, filteredDocumentModels, onSelectDocumentModel, getDocDispatcher, }) => {
|
|
25
|
-
const [selectedStatuses, setSelectedStatuses] = useState([]);
|
|
23
|
+
export const InvoiceTable = ({ files, state, setActiveDocumentId, selected, setSelected, onBatchAction, onDeleteNode, renameNode, filteredDocumentModels, onSelectDocumentModel, getDocDispatcher, selectedStatuses, onStatusChange, }) => {
|
|
26
24
|
const [selectedDrive] = useSelectedDrive();
|
|
27
25
|
const billingDocStates = state
|
|
28
26
|
.filter((doc) => doc.header.documentType === "powerhouse/billing-statement")
|
|
@@ -30,9 +28,6 @@ export const InvoiceTable = ({ files, state, setActiveDocumentId, selected, setS
|
|
|
30
28
|
id: doc.header.id,
|
|
31
29
|
contributor: doc.state.global.contributor,
|
|
32
30
|
}));
|
|
33
|
-
const handleStatusChange = (value) => {
|
|
34
|
-
setSelectedStatuses(Array.isArray(value) ? value : [value]);
|
|
35
|
-
};
|
|
36
31
|
const shouldShowSection = (status) => {
|
|
37
32
|
return selectedStatuses.length === 0 || selectedStatuses.includes(status);
|
|
38
33
|
};
|
|
@@ -242,7 +237,7 @@ export const InvoiceTable = ({ files, state, setActiveDocumentId, selected, setS
|
|
|
242
237
|
onSelectDocumentModel(integrationsDocument);
|
|
243
238
|
}
|
|
244
239
|
};
|
|
245
|
-
return (_jsxs("div", { className: "w-full h-full bg-white rounded-lg p-4 border border-gray-200 shadow-md mt-4 overflow-x-auto", children: [_jsx(HeaderControls, { statusOptions: statusOptions, onStatusChange:
|
|
240
|
+
return (_jsxs("div", { className: "w-full h-full bg-white rounded-lg p-4 border border-gray-200 shadow-md mt-4 overflow-x-auto", children: [_jsx(HeaderControls, { statusOptions: statusOptions, onStatusChange: onStatusChange, onBatchAction: onBatchAction, onExport: handleCSVExport, selectedStatuses: selectedStatuses, createIntegrationsDocument: createIntegrationsDocument, integrationsDoc: integrationsDoc, setActiveDocumentId: setActiveDocumentId }), shouldShowSection("DRAFT") && (_jsx(InvoiceTableSection, { title: "Draft", count: draft.length, onSelectDocumentModel: onSelectDocumentModel, filteredDocumentModels: filteredDocumentModels, children: _jsxs("table", { className: "w-full text-sm border-separate border-spacing-0 border border-gray-400", children: [_jsx("thead", { children: _jsxs("tr", { className: "bg-gray-50", children: [_jsx("th", { className: "px-2 py-2 w-8" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Invoice" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Invoice No." }), _jsx("th", { className: "px-2 py-2 text-center", children: "Issue Date" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Due Date" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Currency" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Amount" }), _jsx("th", { className: "px-2 py-2", children: "Exported" })] }) }), _jsx("tbody", { children: draft.map((row) => (_jsx(InvoiceTableRow, { files: files, row: row, isSelected: !!selected[row.id], onSelect: (checked) => setSelected((prev) => ({
|
|
246
241
|
...prev,
|
|
247
242
|
[row.id]: checked,
|
|
248
243
|
})), setActiveDocumentId: setActiveDocumentId, onDeleteNode: handleDelete, renameNode: renameNode }, row.id))) })] }) })), shouldShowSection("ISSUED") && (_jsx(InvoiceTableSection, { title: "Issued", count: issued.length, children: _jsxs("table", { className: "w-full text-sm border-separate border-spacing-0 border border-gray-400", children: [_jsx("thead", { children: _jsxs("tr", { className: "bg-gray-50", children: [_jsx("th", { className: "px-2 py-2 w-8" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Issuer" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Invoice No." }), _jsx("th", { className: "px-2 py-2 text-center", children: "Issue Date" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Due Date" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Currency" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Amount" }), _jsx("th", { className: "px-2 py-2 text-center", children: "Billing Statement" }), _jsx("th", { className: "px-2 py-2", children: "Exported" })] }) }), _jsx("tbody", { children: issued.map((row) => (_jsx(InvoiceTableRow, { files: files, row: row, isSelected: !!selected[row.id], onSelect: (checked) => setSelected((prev) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvoiceTableRow.d.ts","sourceRoot":"","sources":["../../../../../editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.tsx"],"names":[],"mappings":"AAKA,eAAO,MAAM,eAAe,GAAI,kIAU7B;IACD,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;IACd,GAAG,EAAE,GAAG,CAAC;IACT,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,wBAAwB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,gBAAgB,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D,
|
|
1
|
+
{"version":3,"file":"InvoiceTableRow.d.ts","sourceRoot":"","sources":["../../../../../editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.tsx"],"names":[],"mappings":"AAKA,eAAO,MAAM,eAAe,GAAI,kIAU7B;IACD,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;IACd,GAAG,EAAE,GAAG,CAAC;IACT,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACrC,mBAAmB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,wBAAwB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,gBAAgB,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC1D,4CAoKA,CAAC"}
|
|
@@ -19,12 +19,21 @@ export const InvoiceTableRow = ({ files, row, isSelected, onSelect, setActiveDoc
|
|
|
19
19
|
const minutes = date.getMinutes().toString().padStart(2, '0');
|
|
20
20
|
return `${day}-${month}-${year} ${hours}:${minutes}`;
|
|
21
21
|
};
|
|
22
|
+
const formatAmount = (amount) => {
|
|
23
|
+
const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
|
|
24
|
+
if (isNaN(numAmount))
|
|
25
|
+
return '0.00';
|
|
26
|
+
return numAmount.toLocaleString('en-US', {
|
|
27
|
+
minimumFractionDigits: 2,
|
|
28
|
+
maximumFractionDigits: 2
|
|
29
|
+
});
|
|
30
|
+
};
|
|
22
31
|
const billingDoc = billingDocStates?.find((doc) => doc.contributor === row.id);
|
|
23
32
|
const billingFile = files?.find((file) => file.id === billingDoc?.id);
|
|
24
33
|
const file = files?.find((file) => file.id === row.id);
|
|
25
34
|
const hasExportedData = row.exported != null && Boolean(row.exported.timestamp?.trim());
|
|
26
35
|
const { onAddFile, onAddFolder, onCopyNode, onDuplicateNode, onMoveNode, onRenameNode, showDeleteNodeModal, } = useDriveContext();
|
|
27
|
-
return (_jsxs("tr", { className: "hover:bg-gray-50", children: [_jsx("td", { className: "px-2 py-2", children: _jsx("input", { type: "checkbox", checked: isSelected, onChange: (e) => onSelect(e.target.checked), className: "size-4 rounded border-gray-300 text-blue-600 focus:ring-2 focus:ring-blue-500" }) }), _jsx("td", { className: "py-1 w-10", children: file && (_jsx(FileItem, { fileNode: file, sharingType: "PUBLIC", onRenameNode: onRenameNode, onDuplicateNode: () => new Promise((resolve) => resolve(undefined)), showDeleteNodeModal: showDeleteNodeModal, isAllowedToCreateDocuments: true, className: "h-10", onAddFile: () => new Promise((resolve) => resolve(undefined)), onAddFolder: () => new Promise((resolve) => resolve(undefined)), onCopyNode: () => new Promise((resolve) => resolve(undefined)), onMoveNode: () => new Promise((resolve) => resolve(undefined)), onAddAndSelectNewFolder: () => new Promise((resolve) => resolve(undefined)), getSyncStatusSync: () => undefined, setSelectedNode: () => setActiveDocumentId(row.id) }, row.id)) }), _jsx("td", { className: "px-2 py-2 text-center", children: row.invoiceNo }), _jsx("td", { className: "px-2 py-2 text-center", children: row.issueDate }), _jsx("td", { className: "px-2 py-2 text-center", children: row.dueDate }), _jsx("td", { className: "px-2 py-2 text-center", children: row.currency }), _jsx("td", { className: "px-2 py-2 text-center", children: row.amount }), (row.status === "ISSUED" ||
|
|
36
|
+
return (_jsxs("tr", { className: "hover:bg-gray-50", children: [_jsx("td", { className: "px-2 py-2", children: _jsx("input", { type: "checkbox", checked: isSelected, onChange: (e) => onSelect(e.target.checked), className: "size-4 rounded border-gray-300 text-blue-600 focus:ring-2 focus:ring-blue-500" }) }), _jsx("td", { className: "py-1 w-10", children: file && (_jsx(FileItem, { fileNode: file, sharingType: "PUBLIC", onRenameNode: onRenameNode, onDuplicateNode: () => new Promise((resolve) => resolve(undefined)), showDeleteNodeModal: showDeleteNodeModal, isAllowedToCreateDocuments: true, className: "h-10", onAddFile: () => new Promise((resolve) => resolve(undefined)), onAddFolder: () => new Promise((resolve) => resolve(undefined)), onCopyNode: () => new Promise((resolve) => resolve(undefined)), onMoveNode: () => new Promise((resolve) => resolve(undefined)), onAddAndSelectNewFolder: () => new Promise((resolve) => resolve(undefined)), getSyncStatusSync: () => undefined, setSelectedNode: () => setActiveDocumentId(row.id) }, row.id)) }), _jsx("td", { className: "px-2 py-2 text-center", children: row.invoiceNo }), _jsx("td", { className: "px-2 py-2 text-center", children: row.issueDate }), _jsx("td", { className: "px-2 py-2 text-center", children: row.dueDate }), _jsx("td", { className: "px-2 py-2 text-center", children: row.currency }), _jsx("td", { className: "px-2 py-2 text-center", children: formatAmount(row.amount) }), (row.status === "ISSUED" ||
|
|
28
37
|
row.status === "ACCEPTED" ||
|
|
29
38
|
row.status === "PAYMENTSCHEDULED" ||
|
|
30
39
|
row.status === "PAYMENTRECEIVED" ||
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches the exchange rate between two currencies using ExchangeRate-API.
|
|
3
|
+
* Supports both fiat and crypto currencies.
|
|
4
|
+
* @param fromCurrency - The currency code to convert from (e.g., 'USD', 'DAI').
|
|
5
|
+
* @param toCurrency - The currency code to convert to (e.g., 'EUR', 'USDS').
|
|
6
|
+
* @returns The exchange rate from fromCurrency to toCurrency.
|
|
7
|
+
*/
|
|
8
|
+
export declare const getExchangeRate: (fromCurrency: string, toCurrency: string) => Promise<number>;
|
|
9
|
+
//# sourceMappingURL=util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../editors/contributor-billing/util.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAU,cAAc,MAAM,EAAE,YAAY,MAAM,KAAG,OAAO,CAAC,MAAM,CAiF9F,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Cache for exchange rates to avoid repeated API calls
|
|
2
|
+
const exchangeRateCache = {};
|
|
3
|
+
/**
|
|
4
|
+
* Fetches the exchange rate between two currencies using ExchangeRate-API.
|
|
5
|
+
* Supports both fiat and crypto currencies.
|
|
6
|
+
* @param fromCurrency - The currency code to convert from (e.g., 'USD', 'DAI').
|
|
7
|
+
* @param toCurrency - The currency code to convert to (e.g., 'EUR', 'USDS').
|
|
8
|
+
* @returns The exchange rate from fromCurrency to toCurrency.
|
|
9
|
+
*/
|
|
10
|
+
export const getExchangeRate = async (fromCurrency, toCurrency) => {
|
|
11
|
+
// Return 1 if currencies are the same
|
|
12
|
+
if (fromCurrency === toCurrency) {
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
15
|
+
// Create cache key
|
|
16
|
+
const cacheKey = `${fromCurrency}_${toCurrency}`;
|
|
17
|
+
// Return cached rate if available
|
|
18
|
+
if (exchangeRateCache[cacheKey] !== undefined) {
|
|
19
|
+
return exchangeRateCache[cacheKey];
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
// Use ExchangeRate-API.com for currency conversion
|
|
23
|
+
// This API supports both fiat and crypto currencies
|
|
24
|
+
const response = await fetch(`https://api.exchangerate-api.com/v4/latest/${fromCurrency}`);
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
throw new Error(`Failed to fetch exchange rates: ${response.status}`);
|
|
27
|
+
}
|
|
28
|
+
const data = await response.json();
|
|
29
|
+
if (!data.rates || !data.rates[toCurrency]) {
|
|
30
|
+
throw new Error(`Exchange rate not found for ${fromCurrency} to ${toCurrency}`);
|
|
31
|
+
}
|
|
32
|
+
const exchangeRate = data.rates[toCurrency];
|
|
33
|
+
// Cache the result
|
|
34
|
+
exchangeRateCache[cacheKey] = exchangeRate;
|
|
35
|
+
return exchangeRate;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error('ExchangeRate-API error:', error);
|
|
39
|
+
// Fallback: try a different approach for crypto currencies
|
|
40
|
+
if (['USDS', 'DAI'].includes(fromCurrency) || ['USDS', 'DAI'].includes(toCurrency)) {
|
|
41
|
+
try {
|
|
42
|
+
// For crypto currencies, use CoinGecko as fallback
|
|
43
|
+
const cryptoMapping = {
|
|
44
|
+
'USDS': 'usd-coin',
|
|
45
|
+
'DAI': 'dai',
|
|
46
|
+
};
|
|
47
|
+
const fromMapped = cryptoMapping[fromCurrency] || fromCurrency.toLowerCase();
|
|
48
|
+
const toMapped = cryptoMapping[toCurrency] || toCurrency.toLowerCase();
|
|
49
|
+
if (cryptoMapping[fromCurrency]) {
|
|
50
|
+
// From crypto to fiat/crypto
|
|
51
|
+
const response = await fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${fromMapped}&vs_currencies=${toMapped}`);
|
|
52
|
+
if (response.ok) {
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
const rate = data[fromMapped]?.[toMapped];
|
|
55
|
+
if (rate) {
|
|
56
|
+
exchangeRateCache[cacheKey] = rate;
|
|
57
|
+
return rate;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (cryptoMapping[toCurrency]) {
|
|
62
|
+
// From fiat to crypto
|
|
63
|
+
const response = await fetch(`https://api.coingecko.com/api/v3/simple/price?ids=${toMapped}&vs_currencies=${fromMapped}`);
|
|
64
|
+
if (response.ok) {
|
|
65
|
+
const data = await response.json();
|
|
66
|
+
const rate = data[toMapped]?.[fromMapped];
|
|
67
|
+
if (rate) {
|
|
68
|
+
const invertedRate = 1 / rate;
|
|
69
|
+
exchangeRateCache[cacheKey] = invertedRate;
|
|
70
|
+
return invertedRate;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (cryptoError) {
|
|
76
|
+
console.error('Crypto fallback error:', cryptoError);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return 1; // Final fallback to 1:1 on error
|
|
80
|
+
}
|
|
81
|
+
};
|
package/dist/style.css
CHANGED
|
@@ -499,6 +499,9 @@
|
|
|
499
499
|
.w-full {
|
|
500
500
|
width: 100%;
|
|
501
501
|
}
|
|
502
|
+
.max-w-\[200px\] {
|
|
503
|
+
max-width: 200px;
|
|
504
|
+
}
|
|
502
505
|
.max-w-full {
|
|
503
506
|
max-width: 100%;
|
|
504
507
|
}
|
|
@@ -545,6 +548,9 @@
|
|
|
545
548
|
.transform {
|
|
546
549
|
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
|
|
547
550
|
}
|
|
551
|
+
.cursor-help {
|
|
552
|
+
cursor: help;
|
|
553
|
+
}
|
|
548
554
|
.cursor-not-allowed {
|
|
549
555
|
cursor: not-allowed;
|
|
550
556
|
}
|
|
@@ -749,6 +755,9 @@
|
|
|
749
755
|
.bg-gray-200 {
|
|
750
756
|
background-color: var(--color-gray-200);
|
|
751
757
|
}
|
|
758
|
+
.bg-gray-300 {
|
|
759
|
+
background-color: var(--color-gray-300);
|
|
760
|
+
}
|
|
752
761
|
.bg-gray-500 {
|
|
753
762
|
background-color: var(--color-gray-500);
|
|
754
763
|
}
|
|
@@ -995,6 +1004,9 @@
|
|
|
995
1004
|
outline-style: var(--tw-outline-style);
|
|
996
1005
|
outline-width: 1px;
|
|
997
1006
|
}
|
|
1007
|
+
.filter {
|
|
1008
|
+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
1009
|
+
}
|
|
998
1010
|
.backdrop-blur-sm {
|
|
999
1011
|
--tw-backdrop-blur: blur(var(--blur-sm));
|
|
1000
1012
|
-webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
|
|
@@ -5472,59 +5484,6 @@ input[type="number"] {
|
|
|
5472
5484
|
syntax: "*";
|
|
5473
5485
|
inherits: false;
|
|
5474
5486
|
}
|
|
5475
|
-
@property --tw-blur {
|
|
5476
|
-
syntax: "*";
|
|
5477
|
-
inherits: false;
|
|
5478
|
-
}
|
|
5479
|
-
@property --tw-brightness {
|
|
5480
|
-
syntax: "*";
|
|
5481
|
-
inherits: false;
|
|
5482
|
-
}
|
|
5483
|
-
@property --tw-contrast {
|
|
5484
|
-
syntax: "*";
|
|
5485
|
-
inherits: false;
|
|
5486
|
-
}
|
|
5487
|
-
@property --tw-grayscale {
|
|
5488
|
-
syntax: "*";
|
|
5489
|
-
inherits: false;
|
|
5490
|
-
}
|
|
5491
|
-
@property --tw-hue-rotate {
|
|
5492
|
-
syntax: "*";
|
|
5493
|
-
inherits: false;
|
|
5494
|
-
}
|
|
5495
|
-
@property --tw-invert {
|
|
5496
|
-
syntax: "*";
|
|
5497
|
-
inherits: false;
|
|
5498
|
-
}
|
|
5499
|
-
@property --tw-opacity {
|
|
5500
|
-
syntax: "*";
|
|
5501
|
-
inherits: false;
|
|
5502
|
-
}
|
|
5503
|
-
@property --tw-saturate {
|
|
5504
|
-
syntax: "*";
|
|
5505
|
-
inherits: false;
|
|
5506
|
-
}
|
|
5507
|
-
@property --tw-sepia {
|
|
5508
|
-
syntax: "*";
|
|
5509
|
-
inherits: false;
|
|
5510
|
-
}
|
|
5511
|
-
@property --tw-drop-shadow {
|
|
5512
|
-
syntax: "*";
|
|
5513
|
-
inherits: false;
|
|
5514
|
-
}
|
|
5515
|
-
@property --tw-drop-shadow-color {
|
|
5516
|
-
syntax: "*";
|
|
5517
|
-
inherits: false;
|
|
5518
|
-
}
|
|
5519
|
-
@property --tw-drop-shadow-alpha {
|
|
5520
|
-
syntax: "<percentage>";
|
|
5521
|
-
inherits: false;
|
|
5522
|
-
initial-value: 100%;
|
|
5523
|
-
}
|
|
5524
|
-
@property --tw-drop-shadow-size {
|
|
5525
|
-
syntax: "*";
|
|
5526
|
-
inherits: false;
|
|
5527
|
-
}
|
|
5528
5487
|
@property --tw-ease {
|
|
5529
5488
|
syntax: "*";
|
|
5530
5489
|
inherits: false;
|
|
@@ -19011,6 +18970,59 @@ input[type="number"] {
|
|
|
19011
18970
|
inherits: false;
|
|
19012
18971
|
initial-value: solid;
|
|
19013
18972
|
}
|
|
18973
|
+
@property --tw-blur {
|
|
18974
|
+
syntax: "*";
|
|
18975
|
+
inherits: false;
|
|
18976
|
+
}
|
|
18977
|
+
@property --tw-brightness {
|
|
18978
|
+
syntax: "*";
|
|
18979
|
+
inherits: false;
|
|
18980
|
+
}
|
|
18981
|
+
@property --tw-contrast {
|
|
18982
|
+
syntax: "*";
|
|
18983
|
+
inherits: false;
|
|
18984
|
+
}
|
|
18985
|
+
@property --tw-grayscale {
|
|
18986
|
+
syntax: "*";
|
|
18987
|
+
inherits: false;
|
|
18988
|
+
}
|
|
18989
|
+
@property --tw-hue-rotate {
|
|
18990
|
+
syntax: "*";
|
|
18991
|
+
inherits: false;
|
|
18992
|
+
}
|
|
18993
|
+
@property --tw-invert {
|
|
18994
|
+
syntax: "*";
|
|
18995
|
+
inherits: false;
|
|
18996
|
+
}
|
|
18997
|
+
@property --tw-opacity {
|
|
18998
|
+
syntax: "*";
|
|
18999
|
+
inherits: false;
|
|
19000
|
+
}
|
|
19001
|
+
@property --tw-saturate {
|
|
19002
|
+
syntax: "*";
|
|
19003
|
+
inherits: false;
|
|
19004
|
+
}
|
|
19005
|
+
@property --tw-sepia {
|
|
19006
|
+
syntax: "*";
|
|
19007
|
+
inherits: false;
|
|
19008
|
+
}
|
|
19009
|
+
@property --tw-drop-shadow {
|
|
19010
|
+
syntax: "*";
|
|
19011
|
+
inherits: false;
|
|
19012
|
+
}
|
|
19013
|
+
@property --tw-drop-shadow-color {
|
|
19014
|
+
syntax: "*";
|
|
19015
|
+
inherits: false;
|
|
19016
|
+
}
|
|
19017
|
+
@property --tw-drop-shadow-alpha {
|
|
19018
|
+
syntax: "<percentage>";
|
|
19019
|
+
inherits: false;
|
|
19020
|
+
initial-value: 100%;
|
|
19021
|
+
}
|
|
19022
|
+
@property --tw-drop-shadow-size {
|
|
19023
|
+
syntax: "*";
|
|
19024
|
+
inherits: false;
|
|
19025
|
+
}
|
|
19014
19026
|
@property --tw-backdrop-blur {
|
|
19015
19027
|
syntax: "*";
|
|
19016
19028
|
inherits: false;
|
|
@@ -19113,6 +19125,19 @@ input[type="number"] {
|
|
|
19113
19125
|
--tw-ring-offset-color: #fff;
|
|
19114
19126
|
--tw-ring-offset-shadow: 0 0 #0000;
|
|
19115
19127
|
--tw-outline-style: solid;
|
|
19128
|
+
--tw-blur: initial;
|
|
19129
|
+
--tw-brightness: initial;
|
|
19130
|
+
--tw-contrast: initial;
|
|
19131
|
+
--tw-grayscale: initial;
|
|
19132
|
+
--tw-hue-rotate: initial;
|
|
19133
|
+
--tw-invert: initial;
|
|
19134
|
+
--tw-opacity: initial;
|
|
19135
|
+
--tw-saturate: initial;
|
|
19136
|
+
--tw-sepia: initial;
|
|
19137
|
+
--tw-drop-shadow: initial;
|
|
19138
|
+
--tw-drop-shadow-color: initial;
|
|
19139
|
+
--tw-drop-shadow-alpha: 100%;
|
|
19140
|
+
--tw-drop-shadow-size: initial;
|
|
19116
19141
|
--tw-backdrop-blur: initial;
|
|
19117
19142
|
--tw-backdrop-brightness: initial;
|
|
19118
19143
|
--tw-backdrop-contrast: initial;
|
|
@@ -19129,19 +19154,6 @@ input[type="number"] {
|
|
|
19129
19154
|
--tw-divide-y-reverse: 0;
|
|
19130
19155
|
--tw-leading: initial;
|
|
19131
19156
|
--tw-tracking: initial;
|
|
19132
|
-
--tw-blur: initial;
|
|
19133
|
-
--tw-brightness: initial;
|
|
19134
|
-
--tw-contrast: initial;
|
|
19135
|
-
--tw-grayscale: initial;
|
|
19136
|
-
--tw-hue-rotate: initial;
|
|
19137
|
-
--tw-invert: initial;
|
|
19138
|
-
--tw-opacity: initial;
|
|
19139
|
-
--tw-saturate: initial;
|
|
19140
|
-
--tw-sepia: initial;
|
|
19141
|
-
--tw-drop-shadow: initial;
|
|
19142
|
-
--tw-drop-shadow-color: initial;
|
|
19143
|
-
--tw-drop-shadow-alpha: 100%;
|
|
19144
|
-
--tw-drop-shadow-size: initial;
|
|
19145
19157
|
--tw-ease: initial;
|
|
19146
19158
|
--tw-content: "";
|
|
19147
19159
|
--tw-gradient-position: initial;
|
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.0.
|
|
4
|
+
"version": "0.0.80",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"@google-cloud/documentai": "^8.12.0",
|
|
60
|
-
"@powerhousedao/builder-tools": "^5.0.0-staging.
|
|
61
|
-
"@powerhousedao/common": "^5.0.0-staging.
|
|
62
|
-
"@powerhousedao/design-system": "^5.0.0-staging.
|
|
60
|
+
"@powerhousedao/builder-tools": "^5.0.0-staging.6",
|
|
61
|
+
"@powerhousedao/common": "^5.0.0-staging.6",
|
|
62
|
+
"@powerhousedao/design-system": "^5.0.0-staging.6",
|
|
63
63
|
"@powerhousedao/document-engineering": "^1.34.0",
|
|
64
64
|
"@react-pdf/renderer": "^4.3.0",
|
|
65
65
|
"@safe-global/api-kit": "^3.0.1",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@types/cors": "^2.8.17",
|
|
70
70
|
"axios": "^1.9.0",
|
|
71
71
|
"cors": "^2.8.5",
|
|
72
|
-
"document-model": "^5.0.0-staging.
|
|
72
|
+
"document-model": "^5.0.0-staging.6",
|
|
73
73
|
"dotenv": "^16.5.0",
|
|
74
74
|
"error": "^10.4.0",
|
|
75
75
|
"ethers": "^6.14.0",
|
|
@@ -85,19 +85,19 @@
|
|
|
85
85
|
"@electric-sql/pglite": "^0.2.12",
|
|
86
86
|
"@eslint/js": "^9.25.0",
|
|
87
87
|
"@powerhousedao/analytics-engine-core": "^0.5.0",
|
|
88
|
-
"@powerhousedao/codegen": "^5.0.0-staging.
|
|
89
|
-
"@powerhousedao/ph-cli": "^5.0.0-staging.
|
|
90
|
-
"@powerhousedao/reactor-api": "^5.0.0-staging.
|
|
91
|
-
"@powerhousedao/reactor-browser": "^5.0.0-staging.
|
|
92
|
-
"@powerhousedao/reactor-local": "^5.0.0-staging.
|
|
88
|
+
"@powerhousedao/codegen": "^5.0.0-staging.6",
|
|
89
|
+
"@powerhousedao/ph-cli": "^5.0.0-staging.6",
|
|
90
|
+
"@powerhousedao/reactor-api": "^5.0.0-staging.6",
|
|
91
|
+
"@powerhousedao/reactor-browser": "^5.0.0-staging.6",
|
|
92
|
+
"@powerhousedao/reactor-local": "^5.0.0-staging.6",
|
|
93
93
|
"@powerhousedao/scalars": "^1.33.1-staging.5",
|
|
94
|
-
"@powerhousedao/switchboard": "^5.0.0-staging.
|
|
94
|
+
"@powerhousedao/switchboard": "^5.0.0-staging.6",
|
|
95
95
|
"@tailwindcss/cli": "^4.1.4",
|
|
96
96
|
"@testing-library/react": "^16.3.0",
|
|
97
97
|
"@types/node": "^22.14.1",
|
|
98
98
|
"@types/react": "^18.3.20",
|
|
99
99
|
"@vitejs/plugin-react": "^4.4.1",
|
|
100
|
-
"document-drive": "^5.0.0-staging.
|
|
100
|
+
"document-drive": "^5.0.0-staging.6",
|
|
101
101
|
"eslint": "^9.25.0",
|
|
102
102
|
"eslint-plugin-react": "^7.37.5",
|
|
103
103
|
"eslint-plugin-react-hooks": "^5.2.0",
|