@powerhousedao/contributor-billing 0.1.37 → 0.1.40
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/account-transactions/gen/schema/types.d.ts +3 -3
- package/dist/document-models/account-transactions/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/accounts/gen/schema/types.d.ts +7 -7
- package/dist/document-models/accounts/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/billing-statement/gen/document-schema.d.ts +16 -16
- package/dist/document-models/billing-statement/gen/document-schema.d.ts.map +1 -1
- package/dist/document-models/billing-statement/gen/schema/types.d.ts +5 -5
- package/dist/document-models/billing-statement/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/expense-report/gen/document-model.js +1 -1
- package/dist/document-models/expense-report/gen/document-schema.d.ts +16 -16
- package/dist/document-models/expense-report/gen/ph-factories.d.ts.map +1 -1
- package/dist/document-models/expense-report/gen/ph-factories.js +5 -0
- package/dist/document-models/expense-report/gen/schema/types.d.ts +2 -2
- package/dist/document-models/expense-report/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/expense-report/gen/schema/zod.d.ts.map +1 -1
- package/dist/document-models/expense-report/gen/schema/zod.js +10 -30
- package/dist/document-models/expense-report/gen/utils.d.ts.map +1 -1
- package/dist/document-models/expense-report/gen/utils.js +5 -0
- package/dist/document-models/expense-report/src/reducers/wallet.d.ts.map +1 -1
- package/dist/document-models/expense-report/src/reducers/wallet.js +61 -0
- package/dist/document-models/invoice/gen/document-schema.d.ts +32 -32
- package/dist/document-models/invoice/gen/document-schema.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/schema/types.d.ts +10 -10
- package/dist/document-models/invoice/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/invoice/gen/schema/zod.d.ts.map +1 -1
- package/dist/document-models/service-offering/gen/document-schema.d.ts +16 -16
- package/dist/document-models/service-offering/gen/document-schema.d.ts.map +1 -1
- package/dist/document-models/service-offering/gen/schema/types.d.ts +11 -11
- package/dist/document-models/service-offering/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/service-subscriptions/gen/schema/types.d.ts +6 -6
- package/dist/document-models/service-subscriptions/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/snapshot-report/gen/document-schema.d.ts.map +1 -1
- package/dist/document-models/snapshot-report/gen/schema/types.d.ts +8 -8
- package/dist/document-models/snapshot-report/gen/schema/types.d.ts.map +1 -1
- package/dist/document-models/snapshot-report/src/reducers/configuration.d.ts.map +1 -1
- package/dist/document-models/snapshot-report/src/reducers/configuration.js +1 -1
- package/dist/editors/accounts-editor/components/AccountForm.d.ts.map +1 -1
- package/dist/editors/accounts-editor/components/AccountForm.js +3 -1
- package/dist/editors/builder-team-admin/components/FolderTree.d.ts.map +1 -1
- package/dist/editors/builder-team-admin/components/FolderTree.js +2 -2
- package/dist/editors/contributor-billing/components/AddMonthButton.d.ts +5 -0
- package/dist/editors/contributor-billing/components/AddMonthButton.d.ts.map +1 -0
- package/dist/editors/contributor-billing/components/AddMonthButton.js +56 -0
- package/dist/editors/contributor-billing/components/BillingOverview.d.ts +10 -0
- package/dist/editors/contributor-billing/components/BillingOverview.d.ts.map +1 -0
- package/dist/editors/contributor-billing/components/BillingOverview.js +117 -0
- package/dist/editors/contributor-billing/components/DashboardHome.d.ts +11 -0
- package/dist/editors/contributor-billing/components/DashboardHome.d.ts.map +1 -0
- package/dist/editors/contributor-billing/components/DashboardHome.js +51 -0
- package/dist/editors/contributor-billing/components/DriveContents.d.ts +8 -2
- package/dist/editors/contributor-billing/components/DriveContents.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/DriveContents.js +24 -10
- package/dist/editors/contributor-billing/components/DriveExplorer.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/DriveExplorer.js +18 -3
- package/dist/editors/contributor-billing/components/FolderTree.d.ts +19 -9
- package/dist/editors/contributor-billing/components/FolderTree.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/FolderTree.js +200 -103
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.d.ts +8 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderControls.js +5 -3
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.d.ts +6 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.js +14 -6
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts +6 -2
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.js +68 -19
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableContainer.d.ts +10 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableContainer.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableContainer.js +145 -32
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableRow.js +6 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableSection.d.ts +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableSection.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTableSection.js +33 -7
- package/dist/editors/contributor-billing/components/MonthOverview.d.ts +12 -0
- package/dist/editors/contributor-billing/components/MonthOverview.d.ts.map +1 -0
- package/dist/editors/contributor-billing/components/MonthOverview.js +35 -0
- package/dist/editors/contributor-billing/components/MonthlyReporting.d.ts +13 -0
- package/dist/editors/contributor-billing/components/MonthlyReporting.d.ts.map +1 -0
- package/dist/editors/contributor-billing/components/MonthlyReporting.js +90 -0
- package/dist/editors/contributor-billing/components/ReportingView.d.ts +10 -0
- package/dist/editors/contributor-billing/components/ReportingView.d.ts.map +1 -0
- package/dist/editors/contributor-billing/components/ReportingView.js +112 -0
- package/dist/editors/contributor-billing/config.js +1 -1
- package/dist/editors/contributor-billing/hooks/useBillingFolderStructure.d.ts +54 -0
- package/dist/editors/contributor-billing/hooks/useBillingFolderStructure.d.ts.map +1 -0
- package/dist/editors/contributor-billing/hooks/useBillingFolderStructure.js +145 -0
- package/dist/editors/expense-report/components/AddBillingStatementModal.d.ts +3 -1
- package/dist/editors/expense-report/components/AddBillingStatementModal.d.ts.map +1 -1
- package/dist/editors/expense-report/components/AddBillingStatementModal.js +23 -7
- package/dist/editors/expense-report/components/AggregatedExpensesTable.js +2 -2
- package/dist/editors/expense-report/components/ExpenseReportPDF.js +2 -2
- package/dist/editors/expense-report/editor.d.ts.map +1 -1
- package/dist/editors/expense-report/editor.js +70 -14
- package/dist/editors/expense-report/hooks/useSyncWallet.js +9 -9
- package/dist/editors/invoice/ingestPDF.js +1 -1
- package/dist/editors/invoice/invoiceToGnosis.js +2 -2
- package/dist/editors/invoice/requestFinance.js +2 -2
- package/dist/editors/invoice/uploadPdfChunked.js +2 -2
- package/dist/editors/snapshot-report-editor/editor.d.ts.map +1 -1
- package/dist/editors/snapshot-report-editor/editor.js +26 -5
- package/dist/scripts/invoice/pdfToClaudeAI.d.ts.map +1 -1
- package/dist/scripts/invoice/pdfToClaudeAI.js +3 -1
- package/dist/style.css +85 -0
- package/dist/subgraphs/budget-statements/index.d.ts +11 -0
- package/dist/subgraphs/budget-statements/index.d.ts.map +1 -0
- package/dist/subgraphs/budget-statements/index.js +11 -0
- package/dist/subgraphs/budget-statements/resolvers.d.ts +3 -0
- package/dist/subgraphs/budget-statements/resolvers.d.ts.map +1 -0
- package/dist/subgraphs/budget-statements/resolvers.js +400 -0
- package/dist/subgraphs/budget-statements/schema.d.ts +3 -0
- package/dist/subgraphs/budget-statements/schema.d.ts.map +1 -0
- package/dist/subgraphs/budget-statements/schema.js +132 -0
- package/dist/subgraphs/index.d.ts +1 -0
- package/dist/subgraphs/index.d.ts.map +1 -1
- package/dist/subgraphs/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { FileText, Camera, Calendar } from "lucide-react";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { useDocumentsInSelectedDrive, setSelectedNode, } from "@powerhousedao/reactor-browser";
|
|
5
|
+
import { useBillingFolderStructure, formatMonthName, } from "../hooks/useBillingFolderStructure.js";
|
|
6
|
+
/**
|
|
7
|
+
* Get color classes for report status badge
|
|
8
|
+
*/
|
|
9
|
+
function getStatusColors(status) {
|
|
10
|
+
const statusLower = status?.toLowerCase() || "draft";
|
|
11
|
+
switch (statusLower) {
|
|
12
|
+
case "final":
|
|
13
|
+
case "approved":
|
|
14
|
+
case "completed":
|
|
15
|
+
return { bg: "bg-green-100", text: "text-green-700" };
|
|
16
|
+
case "submitted":
|
|
17
|
+
case "review":
|
|
18
|
+
case "in_review":
|
|
19
|
+
return { bg: "bg-blue-100", text: "text-blue-700" };
|
|
20
|
+
case "rejected":
|
|
21
|
+
case "cancelled":
|
|
22
|
+
return { bg: "bg-red-100", text: "text-red-700" };
|
|
23
|
+
case "draft":
|
|
24
|
+
default:
|
|
25
|
+
return { bg: "bg-amber-100", text: "text-amber-700" };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Reusable Monthly Reporting component
|
|
30
|
+
* Shows expense and snapshot report status for each month
|
|
31
|
+
*/
|
|
32
|
+
export function MonthlyReporting({ onFolderSelect, showAllMonths = false, }) {
|
|
33
|
+
const documentsInDrive = useDocumentsInSelectedDrive();
|
|
34
|
+
const { monthFolders } = useBillingFolderStructure();
|
|
35
|
+
// Get current and prior month names
|
|
36
|
+
const { currentMonth, priorMonth } = useMemo(() => {
|
|
37
|
+
const now = new Date();
|
|
38
|
+
const current = formatMonthName(now);
|
|
39
|
+
const prior = formatMonthName(new Date(now.getFullYear(), now.getMonth() - 1, 1));
|
|
40
|
+
return { currentMonth: current, priorMonth: prior };
|
|
41
|
+
}, []);
|
|
42
|
+
// Get report info for a specific month
|
|
43
|
+
const getReportInfo = (month, type) => {
|
|
44
|
+
const emptyColors = { bg: "bg-gray-100", text: "text-gray-500" };
|
|
45
|
+
const emptyReport = {
|
|
46
|
+
exists: false,
|
|
47
|
+
status: null,
|
|
48
|
+
colors: emptyColors,
|
|
49
|
+
};
|
|
50
|
+
if (!documentsInDrive)
|
|
51
|
+
return emptyReport;
|
|
52
|
+
const doc = documentsInDrive.find((d) => d.header.documentType === type &&
|
|
53
|
+
d.header.name?.toLowerCase().includes(month.toLowerCase()));
|
|
54
|
+
if (!doc)
|
|
55
|
+
return emptyReport;
|
|
56
|
+
const status = doc.state?.global?.status ||
|
|
57
|
+
"Draft";
|
|
58
|
+
return { exists: true, status, colors: getStatusColors(status) };
|
|
59
|
+
};
|
|
60
|
+
// Get months to display
|
|
61
|
+
const monthsToDisplay = useMemo(() => {
|
|
62
|
+
if (showAllMonths) {
|
|
63
|
+
// Show all months sorted by date (most recent first)
|
|
64
|
+
return Array.from(monthFolders.keys()).sort((a, b) => {
|
|
65
|
+
const dateA = new Date(a);
|
|
66
|
+
const dateB = new Date(b);
|
|
67
|
+
return dateB.getTime() - dateA.getTime();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// Just show current and prior month
|
|
71
|
+
return [currentMonth, priorMonth];
|
|
72
|
+
}, [showAllMonths, monthFolders, currentMonth, priorMonth]);
|
|
73
|
+
const handleOpenMonth = (monthName) => {
|
|
74
|
+
const monthInfo = monthFolders.get(monthName);
|
|
75
|
+
if (monthInfo?.reportingFolder) {
|
|
76
|
+
setSelectedNode(monthInfo.reportingFolder.id);
|
|
77
|
+
onFolderSelect?.({
|
|
78
|
+
folderId: monthInfo.reportingFolder.id,
|
|
79
|
+
folderType: "reporting",
|
|
80
|
+
monthName,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
return (_jsxs("div", { className: "bg-white rounded-xl border border-gray-200 p-6", children: [_jsxs("div", { className: "flex items-center gap-3 mb-5", children: [_jsx("div", { className: "p-2 bg-purple-100 rounded-lg", children: _jsx(Calendar, { className: "w-5 h-5 text-purple-600" }) }), _jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Monthly Reporting" }), _jsx("p", { className: "text-sm text-gray-600", children: "Track expense and snapshot reports for each period" })] })] }), _jsx("div", { className: "space-y-4", children: monthsToDisplay.map((monthName) => {
|
|
85
|
+
const expenseReport = getReportInfo(monthName, "powerhouse/expense-report");
|
|
86
|
+
const snapshotReport = getReportInfo(monthName, "powerhouse/snapshot-report");
|
|
87
|
+
const monthExists = monthFolders.has(monthName);
|
|
88
|
+
return (_jsxs("div", { className: "border border-gray-100 rounded-lg p-4", children: [_jsxs("div", { className: "flex items-center justify-between mb-3", children: [_jsx("h3", { className: "font-medium text-gray-900", children: monthName }), monthExists ? (_jsx("button", { onClick: () => handleOpenMonth(monthName), className: "text-sm text-blue-600 hover:text-blue-700 font-medium", children: "Open Reporting" })) : (_jsx("span", { className: "text-xs text-gray-400", children: "Month not created" }))] }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: `flex items-center gap-2 p-2 rounded ${expenseReport.exists ? "bg-blue-50" : "bg-gray-50"}`, children: [_jsx(FileText, { className: `w-4 h-4 ${expenseReport.exists ? "text-blue-600" : "text-gray-400"}` }), _jsx("span", { className: `text-sm ${expenseReport.exists ? "text-blue-700" : "text-gray-500"}`, children: "Expense Report" }), expenseReport.exists && (_jsx("span", { className: `text-xs font-medium px-1.5 py-0.5 rounded ml-auto ${expenseReport.colors.bg} ${expenseReport.colors.text}`, children: expenseReport.status }))] }), _jsxs("div", { className: `flex items-center gap-2 p-2 rounded ${snapshotReport.exists ? "bg-purple-50" : "bg-gray-50"}`, children: [_jsx(Camera, { className: `w-4 h-4 ${snapshotReport.exists ? "text-purple-600" : "text-gray-400"}` }), _jsx("span", { className: `text-sm ${snapshotReport.exists ? "text-purple-700" : "text-gray-500"}`, children: "Snapshot Report" }), snapshotReport.exists && (_jsx("span", { className: `text-xs font-medium px-1.5 py-0.5 rounded ml-auto ${snapshotReport.colors.bg} ${snapshotReport.colors.text}`, children: snapshotReport.status }))] })] }), !expenseReport.exists && !snapshotReport.exists && (_jsx("div", { className: "mt-3 pt-3 border-t border-gray-100", children: _jsxs("span", { className: "text-xs font-medium text-amber-600 bg-amber-50 px-2 py-1 rounded", children: ["Reports pending for ", monthName] }) }))] }, monthName));
|
|
89
|
+
}) })] }));
|
|
90
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface ReportingViewProps {
|
|
2
|
+
folderId: string;
|
|
3
|
+
monthName?: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* View for the Reporting folder showing Expense Reports and Snapshot Reports
|
|
7
|
+
*/
|
|
8
|
+
export declare function ReportingView({ folderId, monthName }: ReportingViewProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=ReportingView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReportingView.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/ReportingView.tsx"],"names":[],"mappings":"AAcA,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,kBAAkB,2CA0PxE"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useDocumentsInSelectedDrive, setSelectedNode, addDocument, dispatchActions, useSelectedDrive, } from "@powerhousedao/reactor-browser";
|
|
3
|
+
import { useMemo, useState } from "react";
|
|
4
|
+
import { FileText, Camera, Plus } from "lucide-react";
|
|
5
|
+
import { setName } from "document-model";
|
|
6
|
+
import { moveNode } from "document-drive";
|
|
7
|
+
import { actions as expenseReportActions } from "../../../document-models/expense-report/index.js";
|
|
8
|
+
import { actions as snapshotReportActions } from "../../../document-models/snapshot-report/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Parse a month name like "January 2026" into start and end dates
|
|
11
|
+
*/
|
|
12
|
+
function parseMonthDates(monthName) {
|
|
13
|
+
const date = new Date(monthName + " 1"); // e.g., "January 2026 1"
|
|
14
|
+
if (isNaN(date.getTime()))
|
|
15
|
+
return null;
|
|
16
|
+
const start = new Date(date.getFullYear(), date.getMonth(), 1);
|
|
17
|
+
const end = new Date(date.getFullYear(), date.getMonth() + 1, 0); // Last day of month
|
|
18
|
+
return { start, end };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* View for the Reporting folder showing Expense Reports and Snapshot Reports
|
|
22
|
+
*/
|
|
23
|
+
export function ReportingView({ folderId, monthName }) {
|
|
24
|
+
const documentsInDrive = useDocumentsInSelectedDrive();
|
|
25
|
+
const [selectedDrive] = useSelectedDrive();
|
|
26
|
+
const [isCreating, setIsCreating] = useState(false);
|
|
27
|
+
// Find expense reports and snapshot reports that match this month
|
|
28
|
+
const { expenseReports, snapshotReports } = useMemo(() => {
|
|
29
|
+
if (!documentsInDrive || !monthName) {
|
|
30
|
+
return { expenseReports: [], snapshotReports: [] };
|
|
31
|
+
}
|
|
32
|
+
const monthLower = monthName.toLowerCase();
|
|
33
|
+
const expense = documentsInDrive.filter((doc) => doc.header.documentType === "powerhouse/expense-report" &&
|
|
34
|
+
doc.header.name?.toLowerCase().includes(monthLower));
|
|
35
|
+
const snapshot = documentsInDrive.filter((doc) => doc.header.documentType === "powerhouse/snapshot-report" &&
|
|
36
|
+
doc.header.name?.toLowerCase().includes(monthLower));
|
|
37
|
+
return { expenseReports: expense, snapshotReports: snapshot };
|
|
38
|
+
}, [documentsInDrive, monthName]);
|
|
39
|
+
const handleOpenDocument = (docId) => {
|
|
40
|
+
setSelectedNode(docId);
|
|
41
|
+
};
|
|
42
|
+
const driveId = selectedDrive?.header.id;
|
|
43
|
+
const handleCreateExpenseReport = async () => {
|
|
44
|
+
if (!driveId || !monthName || isCreating)
|
|
45
|
+
return;
|
|
46
|
+
setIsCreating(true);
|
|
47
|
+
try {
|
|
48
|
+
const reportName = `${monthName} - Expense Report`;
|
|
49
|
+
const createdNode = await addDocument(driveId, reportName, "powerhouse/expense-report", undefined, undefined, undefined, "powerhouse-expense-report-editor");
|
|
50
|
+
if (!createdNode?.id)
|
|
51
|
+
return;
|
|
52
|
+
// Move to reporting folder
|
|
53
|
+
if (folderId) {
|
|
54
|
+
await dispatchActions(moveNode({ srcFolder: createdNode.id, targetParentFolder: folderId }), driveId);
|
|
55
|
+
}
|
|
56
|
+
// Set the document name
|
|
57
|
+
await dispatchActions(setName(reportName), createdNode.id);
|
|
58
|
+
// Set period dates based on month
|
|
59
|
+
const dates = parseMonthDates(monthName);
|
|
60
|
+
if (dates) {
|
|
61
|
+
await dispatchActions([
|
|
62
|
+
expenseReportActions.setPeriodStart({
|
|
63
|
+
periodStart: dates.start.toISOString(),
|
|
64
|
+
}),
|
|
65
|
+
expenseReportActions.setPeriodEnd({
|
|
66
|
+
periodEnd: dates.end.toISOString(),
|
|
67
|
+
}),
|
|
68
|
+
], createdNode.id);
|
|
69
|
+
}
|
|
70
|
+
// Open the created report
|
|
71
|
+
setSelectedNode(createdNode.id);
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
setIsCreating(false);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const handleCreateSnapshotReport = async () => {
|
|
78
|
+
if (!driveId || !monthName || isCreating)
|
|
79
|
+
return;
|
|
80
|
+
setIsCreating(true);
|
|
81
|
+
try {
|
|
82
|
+
const reportName = `${monthName} - Snapshot Report`;
|
|
83
|
+
const createdNode = await addDocument(driveId, reportName, "powerhouse/snapshot-report", undefined, undefined, undefined, "powerhouse-snapshot-report-editor");
|
|
84
|
+
if (!createdNode?.id)
|
|
85
|
+
return;
|
|
86
|
+
// Move to reporting folder
|
|
87
|
+
if (folderId) {
|
|
88
|
+
await dispatchActions(moveNode({ srcFolder: createdNode.id, targetParentFolder: folderId }), driveId);
|
|
89
|
+
}
|
|
90
|
+
// Set the document name
|
|
91
|
+
await dispatchActions(setName(reportName), createdNode.id);
|
|
92
|
+
// Set period dates based on month
|
|
93
|
+
const dates = parseMonthDates(monthName);
|
|
94
|
+
if (dates) {
|
|
95
|
+
await dispatchActions([
|
|
96
|
+
snapshotReportActions.setPeriodStart({
|
|
97
|
+
periodStart: dates.start.toISOString(),
|
|
98
|
+
}),
|
|
99
|
+
snapshotReportActions.setPeriodEnd({
|
|
100
|
+
periodEnd: dates.end.toISOString(),
|
|
101
|
+
}),
|
|
102
|
+
], createdNode.id);
|
|
103
|
+
}
|
|
104
|
+
// Open the created report
|
|
105
|
+
setSelectedNode(createdNode.id);
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
setIsCreating(false);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
return (_jsxs("div", { children: [_jsxs("div", { className: "mb-6", children: [_jsxs("h1", { className: "text-2xl font-bold text-gray-900", children: ["Reporting ", monthName ? `- ${monthName}` : ""] }), _jsxs("p", { className: "text-gray-600", children: ["Manage expense reports and snapshot reports", monthName ? ` for ${monthName}` : ""] })] }), _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6", children: [_jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FileText, { className: "w-5 h-5 text-blue-600" }), _jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Expense Reports" })] }), expenseReports.length === 0 && (_jsxs("button", { onClick: () => void handleCreateExpenseReport(), disabled: isCreating, className: "flex items-center gap-1 px-3 py-1.5 text-sm font-medium text-blue-600 hover:bg-blue-50 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: [_jsx(Plus, { className: "w-4 h-4" }), isCreating ? "Creating..." : "New"] }))] }), expenseReports.length === 0 ? (_jsx("p", { className: "text-gray-500 text-sm", children: "No expense reports yet" })) : (_jsx("div", { className: "space-y-2", children: expenseReports.map((doc) => (_jsxs("button", { onClick: () => handleOpenDocument(doc.header.id), className: "w-full flex items-center gap-3 p-3 text-left hover:bg-gray-50 rounded-md transition-colors border border-gray-100", children: [_jsx(FileText, { className: "w-4 h-4 text-gray-400" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-gray-900 truncate", children: doc.header.name || "Untitled" }), _jsxs("p", { className: "text-xs text-gray-500", children: ["Modified:", " ", new Date(doc.header.lastModifiedAtUtcIso || Date.now()).toLocaleDateString()] })] })] }, doc.header.id))) }))] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Camera, { className: "w-5 h-5 text-purple-600" }), _jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Snapshot Reports" })] }), snapshotReports.length === 0 && (_jsxs("button", { onClick: () => void handleCreateSnapshotReport(), disabled: isCreating, className: "flex items-center gap-1 px-3 py-1.5 text-sm font-medium text-purple-600 hover:bg-purple-50 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed", children: [_jsx(Plus, { className: "w-4 h-4" }), isCreating ? "Creating..." : "New"] }))] }), snapshotReports.length === 0 ? (_jsx("p", { className: "text-gray-500 text-sm", children: "No snapshot reports yet" })) : (_jsx("div", { className: "space-y-2", children: snapshotReports.map((doc) => (_jsxs("button", { onClick: () => handleOpenDocument(doc.header.id), className: "w-full flex items-center gap-3 p-3 text-left hover:bg-gray-50 rounded-md transition-colors border border-gray-100", children: [_jsx(Camera, { className: "w-4 h-4 text-gray-400" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-gray-900 truncate", children: doc.header.name || "Untitled" }), _jsxs("p", { className: "text-xs text-gray-500", children: ["Modified:", " ", new Date(doc.header.lastModifiedAtUtcIso || Date.now()).toLocaleDateString()] })] })] }, doc.header.id))) }))] })] })] }));
|
|
112
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { FolderNode } from "document-drive";
|
|
2
|
+
export interface MonthFolderInfo {
|
|
3
|
+
folder: FolderNode;
|
|
4
|
+
paymentsFolder: FolderNode | null;
|
|
5
|
+
reportingFolder: FolderNode | null;
|
|
6
|
+
}
|
|
7
|
+
export interface UseBillingFolderStructureResult {
|
|
8
|
+
/** The Billing root folder node, or null if it doesn't exist yet */
|
|
9
|
+
billingFolder: FolderNode | null;
|
|
10
|
+
/** Map of month name to folder info (e.g., "January 2025" -> folder info) */
|
|
11
|
+
monthFolders: Map<string, MonthFolderInfo>;
|
|
12
|
+
/** The current month's folder info, or null if not created yet */
|
|
13
|
+
currentMonthFolder: MonthFolderInfo | null;
|
|
14
|
+
/** Create the Billing folder if it doesn't exist */
|
|
15
|
+
createBillingFolder: () => Promise<void>;
|
|
16
|
+
/** Create a new month folder with Payments and Reporting subfolders */
|
|
17
|
+
createMonthFolder: (monthName: string) => Promise<void>;
|
|
18
|
+
/** Check if a folder is a Payments folder */
|
|
19
|
+
isPaymentsFolder: (folderId: string) => boolean;
|
|
20
|
+
/** Check if a folder is a Reporting folder */
|
|
21
|
+
isReportingFolder: (folderId: string) => boolean;
|
|
22
|
+
/** Get all Payments folder IDs */
|
|
23
|
+
paymentsFolderIds: Set<string>;
|
|
24
|
+
/** Get all Reporting folder IDs */
|
|
25
|
+
reportingFolderIds: Set<string>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Format a date as "January 2025"
|
|
29
|
+
*/
|
|
30
|
+
export declare function formatMonthName(date: Date): string;
|
|
31
|
+
/**
|
|
32
|
+
* Get the current month name (e.g., "January 2025")
|
|
33
|
+
*/
|
|
34
|
+
export declare function getCurrentMonthName(): string;
|
|
35
|
+
/**
|
|
36
|
+
* Hook that manages the Billing folder structure in the Contributor Billing drive.
|
|
37
|
+
*
|
|
38
|
+
* Structure:
|
|
39
|
+
* Billing/
|
|
40
|
+
* ├── January 2025/
|
|
41
|
+
* │ ├── Payments/
|
|
42
|
+
* │ └── Reporting/
|
|
43
|
+
* ├── February 2025/
|
|
44
|
+
* │ ├── Payments/
|
|
45
|
+
* │ └── Reporting/
|
|
46
|
+
* └── ...
|
|
47
|
+
*
|
|
48
|
+
* This hook:
|
|
49
|
+
* 1. Creates the "Billing" folder if it doesn't exist
|
|
50
|
+
* 2. Auto-creates the current month folder with Payments/Reporting subfolders
|
|
51
|
+
* 3. Provides a function to manually create additional month folders
|
|
52
|
+
*/
|
|
53
|
+
export declare function useBillingFolderStructure(): UseBillingFolderStructureResult;
|
|
54
|
+
//# sourceMappingURL=useBillingFolderStructure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBillingFolderStructure.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/hooks/useBillingFolderStructure.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAQ,MAAM,gBAAgB,CAAC;AAYvD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,UAAU,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,UAAU,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,+BAA+B;IAC9C,oEAAoE;IACpE,aAAa,EAAE,UAAU,GAAG,IAAI,CAAC;IACjC,6EAA6E;IAC7E,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC3C,kEAAkE;IAClE,kBAAkB,EAAE,eAAe,GAAG,IAAI,CAAC;IAC3C,oDAAoD;IACpD,mBAAmB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,uEAAuE;IACvE,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,6CAA6C;IAC7C,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAChD,8CAA8C;IAC9C,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,kCAAkC;IAClC,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,mCAAmC;IACnC,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAElD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,IAAI,+BAA+B,CAsJ3E"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { useMemo, useCallback } from "react";
|
|
2
|
+
import { isFolderNodeKind, addFolder, useSelectedDrive, } from "@powerhousedao/reactor-browser";
|
|
3
|
+
const BILLING_FOLDER_NAME = "Billing";
|
|
4
|
+
const PAYMENTS_FOLDER_NAME = "Payments";
|
|
5
|
+
const REPORTING_FOLDER_NAME = "Reporting";
|
|
6
|
+
// Module-level tracking to prevent duplicate folder creation across all hook instances
|
|
7
|
+
const globalCreationState = {
|
|
8
|
+
createdBillingFolderForDrives: new Set(),
|
|
9
|
+
creatingMonths: new Set(), // Format: "driveId:monthName"
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Format a date as "January 2025"
|
|
13
|
+
*/
|
|
14
|
+
export function formatMonthName(date) {
|
|
15
|
+
return date.toLocaleDateString("en-US", { month: "long", year: "numeric" });
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Get the current month name (e.g., "January 2025")
|
|
19
|
+
*/
|
|
20
|
+
export function getCurrentMonthName() {
|
|
21
|
+
return formatMonthName(new Date());
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Hook that manages the Billing folder structure in the Contributor Billing drive.
|
|
25
|
+
*
|
|
26
|
+
* Structure:
|
|
27
|
+
* Billing/
|
|
28
|
+
* ├── January 2025/
|
|
29
|
+
* │ ├── Payments/
|
|
30
|
+
* │ └── Reporting/
|
|
31
|
+
* ├── February 2025/
|
|
32
|
+
* │ ├── Payments/
|
|
33
|
+
* │ └── Reporting/
|
|
34
|
+
* └── ...
|
|
35
|
+
*
|
|
36
|
+
* This hook:
|
|
37
|
+
* 1. Creates the "Billing" folder if it doesn't exist
|
|
38
|
+
* 2. Auto-creates the current month folder with Payments/Reporting subfolders
|
|
39
|
+
* 3. Provides a function to manually create additional month folders
|
|
40
|
+
*/
|
|
41
|
+
export function useBillingFolderStructure() {
|
|
42
|
+
const [driveDocument] = useSelectedDrive();
|
|
43
|
+
const driveId = driveDocument?.header.id;
|
|
44
|
+
// Find the "Billing" root folder
|
|
45
|
+
const billingFolder = useMemo(() => {
|
|
46
|
+
if (!driveDocument)
|
|
47
|
+
return null;
|
|
48
|
+
const nodes = driveDocument.state.global.nodes;
|
|
49
|
+
return (nodes.find((node) => isFolderNodeKind(node) && node.name === BILLING_FOLDER_NAME) ?? null);
|
|
50
|
+
}, [driveDocument]);
|
|
51
|
+
// Find all month folders under Billing and their subfolders
|
|
52
|
+
const monthFolders = useMemo(() => {
|
|
53
|
+
const folders = new Map();
|
|
54
|
+
if (!driveDocument || !billingFolder)
|
|
55
|
+
return folders;
|
|
56
|
+
const allNodes = driveDocument.state.global.nodes;
|
|
57
|
+
// Find all folders directly under Billing (these are month folders)
|
|
58
|
+
const monthFolderNodes = allNodes.filter((node) => isFolderNodeKind(node) && node.parentFolder === billingFolder.id);
|
|
59
|
+
for (const monthFolder of monthFolderNodes) {
|
|
60
|
+
// Find Payments and Reporting subfolders
|
|
61
|
+
const paymentsFolder = allNodes.find((node) => isFolderNodeKind(node) &&
|
|
62
|
+
node.parentFolder === monthFolder.id &&
|
|
63
|
+
node.name === PAYMENTS_FOLDER_NAME) ?? null;
|
|
64
|
+
const reportingFolder = allNodes.find((node) => isFolderNodeKind(node) &&
|
|
65
|
+
node.parentFolder === monthFolder.id &&
|
|
66
|
+
node.name === REPORTING_FOLDER_NAME) ?? null;
|
|
67
|
+
folders.set(monthFolder.name, {
|
|
68
|
+
folder: monthFolder,
|
|
69
|
+
paymentsFolder,
|
|
70
|
+
reportingFolder,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return folders;
|
|
74
|
+
}, [driveDocument, billingFolder]);
|
|
75
|
+
// Get current month folder info
|
|
76
|
+
const currentMonthFolder = useMemo(() => {
|
|
77
|
+
const currentMonth = getCurrentMonthName();
|
|
78
|
+
return monthFolders.get(currentMonth) ?? null;
|
|
79
|
+
}, [monthFolders]);
|
|
80
|
+
// Build sets of Payments and Reporting folder IDs for quick lookup
|
|
81
|
+
const { paymentsFolderIds, reportingFolderIds } = useMemo(() => {
|
|
82
|
+
const paymentsIds = new Set();
|
|
83
|
+
const reportingIds = new Set();
|
|
84
|
+
for (const info of monthFolders.values()) {
|
|
85
|
+
if (info.paymentsFolder) {
|
|
86
|
+
paymentsIds.add(info.paymentsFolder.id);
|
|
87
|
+
}
|
|
88
|
+
if (info.reportingFolder) {
|
|
89
|
+
reportingIds.add(info.reportingFolder.id);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { paymentsFolderIds: paymentsIds, reportingFolderIds: reportingIds };
|
|
93
|
+
}, [monthFolders]);
|
|
94
|
+
const isPaymentsFolder = useCallback((folderId) => paymentsFolderIds.has(folderId), [paymentsFolderIds]);
|
|
95
|
+
const isReportingFolder = useCallback((folderId) => reportingFolderIds.has(folderId), [reportingFolderIds]);
|
|
96
|
+
// Create a new month folder with Payments and Reporting subfolders
|
|
97
|
+
const createMonthFolder = useCallback(async (monthName) => {
|
|
98
|
+
if (!driveId || !billingFolder)
|
|
99
|
+
return;
|
|
100
|
+
if (monthFolders.has(monthName))
|
|
101
|
+
return; // Already exists
|
|
102
|
+
const creationKey = `${driveId}:${monthName}`;
|
|
103
|
+
if (globalCreationState.creatingMonths.has(creationKey))
|
|
104
|
+
return; // Already creating
|
|
105
|
+
globalCreationState.creatingMonths.add(creationKey);
|
|
106
|
+
try {
|
|
107
|
+
// Create month folder
|
|
108
|
+
const monthFolder = await addFolder(driveId, monthName, billingFolder.id);
|
|
109
|
+
if (!monthFolder) {
|
|
110
|
+
console.error(`Failed to create month folder: ${monthName}`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Create Payments subfolder
|
|
114
|
+
await addFolder(driveId, PAYMENTS_FOLDER_NAME, monthFolder.id);
|
|
115
|
+
// Create Reporting subfolder
|
|
116
|
+
await addFolder(driveId, REPORTING_FOLDER_NAME, monthFolder.id);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error(`Error creating month folder structure: ${monthName}`, error);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
globalCreationState.creatingMonths.delete(creationKey);
|
|
123
|
+
}
|
|
124
|
+
}, [driveId, billingFolder, monthFolders]);
|
|
125
|
+
// Create the Billing folder if it doesn't exist (called manually)
|
|
126
|
+
const createBillingFolder = useCallback(async () => {
|
|
127
|
+
if (!driveId || billingFolder)
|
|
128
|
+
return;
|
|
129
|
+
if (globalCreationState.createdBillingFolderForDrives.has(driveId))
|
|
130
|
+
return;
|
|
131
|
+
globalCreationState.createdBillingFolderForDrives.add(driveId);
|
|
132
|
+
await addFolder(driveId, BILLING_FOLDER_NAME);
|
|
133
|
+
}, [driveId, billingFolder]);
|
|
134
|
+
return {
|
|
135
|
+
billingFolder,
|
|
136
|
+
monthFolders,
|
|
137
|
+
currentMonthFolder,
|
|
138
|
+
createBillingFolder,
|
|
139
|
+
createMonthFolder,
|
|
140
|
+
isPaymentsFolder,
|
|
141
|
+
isReportingFolder,
|
|
142
|
+
paymentsFolderIds,
|
|
143
|
+
reportingFolderIds,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -5,7 +5,9 @@ interface AddBillingStatementModalProps {
|
|
|
5
5
|
walletAddress: string;
|
|
6
6
|
dispatch: any;
|
|
7
7
|
groups: LineItemGroup[];
|
|
8
|
+
/** The Payments folder ID to filter billing statements by. If not provided, shows all billing statements in the drive. */
|
|
9
|
+
paymentsFolderId?: string | null;
|
|
8
10
|
}
|
|
9
|
-
export declare function AddBillingStatementModal({ isOpen, onClose, walletAddress, dispatch, groups, }: AddBillingStatementModalProps): import("react/jsx-runtime").JSX.Element | null;
|
|
11
|
+
export declare function AddBillingStatementModal({ isOpen, onClose, walletAddress, dispatch, groups, paymentsFolderId, }: AddBillingStatementModalProps): import("react/jsx-runtime").JSX.Element | null;
|
|
10
12
|
export {};
|
|
11
13
|
//# sourceMappingURL=AddBillingStatementModal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddBillingStatementModal.d.ts","sourceRoot":"","sources":["../../../../editors/expense-report/components/AddBillingStatementModal.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AddBillingStatementModal.d.ts","sourceRoot":"","sources":["../../../../editors/expense-report/components/AddBillingStatementModal.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sDAAsD,CAAC;AAI1F,UAAU,6BAA6B;IACrC,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC;IACd,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,0HAA0H;IAC1H,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AA+BD,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,OAAO,EACP,aAAa,EACb,QAAQ,EACR,MAAM,EACN,gBAAgB,GACjB,EAAE,6BAA6B,kDA2V/B"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useMemo } from "react";
|
|
3
3
|
import { Button } from "@powerhousedao/document-engineering";
|
|
4
|
-
import { useDocumentsInSelectedDrive } from "@powerhousedao/reactor-browser";
|
|
4
|
+
import { useDocumentsInSelectedDrive, useSelectedDrive, isFileNodeKind, } from "@powerhousedao/reactor-browser";
|
|
5
5
|
import { generateId } from "document-model";
|
|
6
6
|
import { X, FileText, Check } from "lucide-react";
|
|
7
7
|
import { actions } from "../../../document-models/expense-report/index.js";
|
|
@@ -33,8 +33,9 @@ const fusionLabelToValue = {
|
|
|
33
33
|
"Non-current Liability": "liabilities/non-current",
|
|
34
34
|
Equity: "equity/retained",
|
|
35
35
|
};
|
|
36
|
-
export function AddBillingStatementModal({ isOpen, onClose, walletAddress, dispatch, groups, }) {
|
|
36
|
+
export function AddBillingStatementModal({ isOpen, onClose, walletAddress, dispatch, groups, paymentsFolderId, }) {
|
|
37
37
|
const documents = useDocumentsInSelectedDrive();
|
|
38
|
+
const [driveDocument] = useSelectedDrive();
|
|
38
39
|
const [selectedStatements, setSelectedStatements] = useState(new Set());
|
|
39
40
|
const [searchTerm, setSearchTerm] = useState("");
|
|
40
41
|
// Get already added billing statement IDs for this wallet from documents
|
|
@@ -55,18 +56,33 @@ export function AddBillingStatementModal({ isOpen, onClose, walletAddress, dispa
|
|
|
55
56
|
});
|
|
56
57
|
return ids;
|
|
57
58
|
}, [documents, walletAddress]);
|
|
58
|
-
// Get billing statement documents from the drive
|
|
59
|
+
// Get billing statement documents from the drive, filtered by the Payments folder
|
|
59
60
|
const billingStatements = useMemo(() => {
|
|
60
|
-
if (!documents)
|
|
61
|
+
if (!documents || !driveDocument)
|
|
61
62
|
return [];
|
|
63
|
+
// Get all file nodes from the drive
|
|
64
|
+
const nodes = driveDocument.state.global.nodes;
|
|
65
|
+
// If we have a payments folder ID, filter billing statements to only those in that folder
|
|
66
|
+
// Otherwise, show all billing statements (fallback for editors used outside contributor billing)
|
|
62
67
|
return documents
|
|
63
|
-
.filter((doc) =>
|
|
68
|
+
.filter((doc) => {
|
|
69
|
+
if (doc.header.documentType !== "powerhouse/billing-statement") {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// If we have a payments folder, only include billing statements in that folder
|
|
73
|
+
if (paymentsFolderId) {
|
|
74
|
+
const fileNode = nodes.find((node) => isFileNodeKind(node) && node.id === doc.header.id);
|
|
75
|
+
return fileNode?.parentFolder === paymentsFolderId;
|
|
76
|
+
}
|
|
77
|
+
// Fallback: include all billing statements if no payments folder context
|
|
78
|
+
return true;
|
|
79
|
+
})
|
|
64
80
|
.map((doc) => ({
|
|
65
81
|
id: doc.header.id,
|
|
66
82
|
name: doc.header.name,
|
|
67
83
|
document: doc, // Full document with state
|
|
68
84
|
}));
|
|
69
|
-
}, [documents]);
|
|
85
|
+
}, [documents, driveDocument, paymentsFolderId]);
|
|
70
86
|
// Filter billing statements based on search term
|
|
71
87
|
const filteredStatements = useMemo(() => {
|
|
72
88
|
if (!searchTerm.trim())
|
|
@@ -144,7 +160,7 @@ export function AddBillingStatementModal({ isOpen, onClose, walletAddress, dispa
|
|
|
144
160
|
const group = groups.find((g) => g.id === groupId);
|
|
145
161
|
categoryAggregation.set(categoryKey, {
|
|
146
162
|
groupId: groupId,
|
|
147
|
-
groupLabel: group?.label || "
|
|
163
|
+
groupLabel: group?.label || "Uncategorized",
|
|
148
164
|
budget: 0,
|
|
149
165
|
actuals: billingLineItem.totalPriceCash || 0,
|
|
150
166
|
forecast: 0,
|
|
@@ -191,7 +191,7 @@ export function AggregatedExpensesTable({ wallets, groups, periodStart, periodEn
|
|
|
191
191
|
items.push({
|
|
192
192
|
lineItemId: item.id || "",
|
|
193
193
|
groupId: item.group || "uncategorized",
|
|
194
|
-
groupLabel: item.groupLabel || "
|
|
194
|
+
groupLabel: item.groupLabel || "Uncategorized",
|
|
195
195
|
parentGroupId: item.parentGroupId,
|
|
196
196
|
parentGroupLabel: item.parentGroupLabel,
|
|
197
197
|
budget: item.budget || 0,
|
|
@@ -341,7 +341,7 @@ export function AggregatedExpensesTable({ wallets, groups, periodStart, periodEn
|
|
|
341
341
|
return null;
|
|
342
342
|
const subtotals = calculateSubtotal(items);
|
|
343
343
|
const parentLabel = parentKey === "uncategorized"
|
|
344
|
-
? "
|
|
344
|
+
? "Uncategorized"
|
|
345
345
|
: items[0]?.parentGroupLabel || "Other";
|
|
346
346
|
return (_jsxs(React.Fragment, { children: [_jsx("tr", { className: "bg-gray-100 dark:bg-gray-800", children: _jsx("td", { colSpan: 8, className: "px-6 py-3 text-sm font-bold text-gray-900 dark:text-white", children: parentLabel }) }), items.map((item) => {
|
|
347
347
|
if (!item)
|
|
@@ -220,7 +220,7 @@ export function ExpenseReportPDF({ periodStart, periodEnd, wallets, groups, }) {
|
|
|
220
220
|
});
|
|
221
221
|
return entries.map(([key, items]) => ({
|
|
222
222
|
parentLabel: key === "uncategorized"
|
|
223
|
-
? "
|
|
223
|
+
? "Uncategorized"
|
|
224
224
|
: items[0]?.parentGroupLabel || "Unknown",
|
|
225
225
|
items,
|
|
226
226
|
}));
|
|
@@ -264,7 +264,7 @@ export function ExpenseReportPDF({ periodStart, periodEnd, wallets, groups, }) {
|
|
|
264
264
|
: styles.differenceNormal;
|
|
265
265
|
return (_jsxs(View, { style: itemIndex % 2 === 0
|
|
266
266
|
? styles.tableRow
|
|
267
|
-
: styles.tableRowAlt, children: [_jsx(Text, { style: [styles.cell, styles.categoryCol], children: item.groupLabel || item.label || "
|
|
267
|
+
: styles.tableRowAlt, children: [_jsx(Text, { style: [styles.cell, styles.categoryCol], children: item.groupLabel || item.label || "Uncategorized" }), _jsx(Text, { style: [styles.cellRight, styles.budgetCol], children: formatNumber(item.budget) }), _jsx(Text, { style: [styles.cellRight, styles.forecastCol], children: formatNumber(item.forecast) }), _jsx(Text, { style: [styles.cellRight, styles.actualsCol], children: formatNumber(item.actuals) }), _jsx(Text, { style: [
|
|
268
268
|
styles.cellRight,
|
|
269
269
|
styles.differenceCol,
|
|
270
270
|
differenceStyle,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../editors/expense-report/editor.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../editors/expense-report/editor.tsx"],"names":[],"mappings":"AAuEA,MAAM,CAAC,OAAO,UAAU,MAAM,4CAiZ7B"}
|