@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.
Files changed (20) hide show
  1. package/dist/document-models/integrations/gen/ph-factories.js +6 -6
  2. package/dist/document-models/integrations/gen/utils.d.ts.map +1 -1
  3. package/dist/document-models/integrations/gen/utils.js +6 -6
  4. package/dist/editors/contributor-billing/components/DriveExplorer.d.ts.map +1 -1
  5. package/dist/editors/contributor-billing/components/DriveExplorer.js +8 -2
  6. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.d.ts.map +1 -1
  7. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.js +5 -5
  8. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.d.ts +2 -0
  9. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.d.ts.map +1 -0
  10. package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.js +68 -0
  11. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts +3 -1
  12. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts.map +1 -1
  13. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.js +2 -7
  14. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.d.ts.map +1 -1
  15. package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.js +10 -1
  16. package/dist/editors/contributor-billing/util.d.ts +9 -0
  17. package/dist/editors/contributor-billing/util.d.ts.map +1 -0
  18. package/dist/editors/contributor-billing/util.js +81 -0
  19. package/dist/style.css +78 -66
  20. 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
- client_id: "",
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;AAEF,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"}
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
- client_id: "",
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;AASxC;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,gBAAgB,2CA2TpD"}
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,4CAgKA,CAAC"}
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: [_jsx("h3", { className: "text-lg font-bold", children: "Powerhouse OH Admin Drive" }), _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: {
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: "flex gap-2 items-center", children: [_jsx(Select, { style: {
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,2 @@
1
+ export declare const HeaderStats: () => import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=HeaderStats.d.ts.map
@@ -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;CACvC;AAED,eAAO,MAAM,YAAY,GAAI,yKAY1B,iBAAiB,4CA+qBnB,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: handleStatusChange, onBatchAction: onBatchAction, onExport: handleCSVExport, selectedStatuses: selectedInvoiceStatuses, 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) => ({
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,4CA2JA,CAAC"}
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.79",
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.2",
61
- "@powerhousedao/common": "^5.0.0-staging.2",
62
- "@powerhousedao/design-system": "^5.0.0-staging.2",
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.2",
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.2",
89
- "@powerhousedao/ph-cli": "^5.0.0-staging.2",
90
- "@powerhousedao/reactor-api": "^5.0.0-staging.2",
91
- "@powerhousedao/reactor-browser": "^5.0.0-staging.2",
92
- "@powerhousedao/reactor-local": "^5.0.0-staging.2",
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.2",
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.2",
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",