@powerhousedao/contributor-billing 1.0.0-dev.3 → 1.0.0-dev.4

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 (52) hide show
  1. package/dist/document-models/invoice/src/reducers/general.d.ts.map +1 -1
  2. package/dist/document-models/invoice/src/reducers/general.js +8 -0
  3. package/dist/document-models/invoice/src/reducers/transitions.d.ts.map +1 -1
  4. package/dist/document-models/invoice/src/reducers/transitions.js +4 -0
  5. package/dist/editors/builder-team-admin/components/DriveExplorer.d.ts.map +1 -1
  6. package/dist/editors/builder-team-admin/components/DriveExplorer.js +64 -4
  7. package/dist/editors/builder-team-admin/module.js +1 -1
  8. package/dist/editors/contributor-billing/components/DashboardHome.d.ts.map +1 -1
  9. package/dist/editors/contributor-billing/components/DashboardHome.js +2 -8
  10. package/dist/editors/contributor-billing/components/DriveExplorer.d.ts.map +1 -1
  11. package/dist/editors/contributor-billing/components/DriveExplorer.js +52 -1
  12. package/dist/editors/contributor-billing/components/FolderTree.d.ts.map +1 -1
  13. package/dist/editors/contributor-billing/components/FolderTree.js +6 -15
  14. package/dist/editors/contributor-billing/components/MonthReportCard.d.ts +8 -1
  15. package/dist/editors/contributor-billing/components/MonthReportCard.d.ts.map +1 -1
  16. package/dist/editors/contributor-billing/components/MonthReportCard.js +6 -3
  17. package/dist/editors/contributor-billing/components/MonthlyReportsOverview.d.ts +1 -1
  18. package/dist/editors/contributor-billing/components/MonthlyReportsOverview.d.ts.map +1 -1
  19. package/dist/editors/contributor-billing/components/MonthlyReportsOverview.js +56 -3
  20. package/dist/editors/contributor-billing/components/cbToast.d.ts.map +1 -1
  21. package/dist/editors/contributor-billing/hooks/useDocumentAutoPlacement.d.ts.map +1 -1
  22. package/dist/editors/contributor-billing/hooks/useDocumentAutoPlacement.js +42 -2
  23. package/dist/editors/contributor-billing/module.js +1 -1
  24. package/dist/editors/invoice/editor.d.ts.map +1 -1
  25. package/dist/editors/invoice/editor.js +3 -2
  26. package/dist/editors/invoice/legalEntity/legalEntity.d.ts +2 -1
  27. package/dist/editors/invoice/legalEntity/legalEntity.d.ts.map +1 -1
  28. package/dist/editors/invoice/legalEntity/legalEntity.js +2 -2
  29. package/dist/editors/invoice/legalEntity/walletSection.d.ts +1 -0
  30. package/dist/editors/invoice/legalEntity/walletSection.d.ts.map +1 -1
  31. package/dist/editors/invoice/legalEntity/walletSection.js +2 -2
  32. package/dist/editors/invoice/validation/validationHandler.d.ts +1 -1
  33. package/dist/editors/invoice/validation/validationHandler.d.ts.map +1 -1
  34. package/dist/editors/invoice/validation/validationHandler.js +14 -1
  35. package/dist/editors/invoice/validation/validationManager.d.ts.map +1 -1
  36. package/dist/editors/invoice/validation/validationManager.js +3 -1
  37. package/dist/editors/invoice/validation/validationRules.d.ts +2 -0
  38. package/dist/editors/invoice/validation/validationRules.d.ts.map +1 -1
  39. package/dist/editors/invoice/validation/validationRules.js +50 -0
  40. package/dist/style.css +96 -3
  41. package/dist/subgraphs/budget-statements/resolvers.d.ts +38 -0
  42. package/dist/subgraphs/budget-statements/resolvers.d.ts.map +1 -1
  43. package/dist/subgraphs/budget-statements/resolvers.js +151 -44
  44. package/dist/subgraphs/budget-statements/resolvers.test.d.ts +2 -0
  45. package/dist/subgraphs/budget-statements/resolvers.test.d.ts.map +1 -0
  46. package/dist/subgraphs/budget-statements/resolvers.test.js +329 -0
  47. package/dist/subgraphs/budget-statements/schema.d.ts.map +1 -1
  48. package/dist/subgraphs/budget-statements/schema.js +8 -0
  49. package/package.json +16 -16
  50. package/dist/editors/contributor-billing/components/CreateHubProfileModal.d.ts +0 -12
  51. package/dist/editors/contributor-billing/components/CreateHubProfileModal.d.ts.map +0 -1
  52. package/dist/editors/contributor-billing/components/CreateHubProfileModal.js +0 -74
@@ -1 +1 @@
1
- {"version":3,"file":"general.d.ts","sourceRoot":"","sources":["../../../../../document-models/invoice/src/reducers/general.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4DAA4D,CAAC;AAgB3G,eAAO,MAAM,wBAAwB,EAAE,wBA2DtC,CAAC"}
1
+ {"version":3,"file":"general.d.ts","sourceRoot":"","sources":["../../../../../document-models/invoice/src/reducers/general.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4DAA4D,CAAC;AAgB3G,eAAO,MAAM,wBAAwB,EAAE,wBAuEtC,CAAC"}
@@ -33,6 +33,14 @@ export const invoiceGeneralOperations = {
33
33
  state = Object.assign(state, newState);
34
34
  },
35
35
  editStatusOperation(state, action) {
36
+ if (state.status === "DRAFT" &&
37
+ action.input.status !== "DRAFT" &&
38
+ action.input.status !== "CANCELLED") {
39
+ const wallet = state.issuer?.paymentRouting?.wallet;
40
+ if (!wallet?.address || (!wallet.chainName && !wallet.chainId)) {
41
+ throw new Error("Issuer wallet address and chain must be set before moving out of DRAFT");
42
+ }
43
+ }
36
44
  state.status = action.input.status;
37
45
  },
38
46
  editPaymentDataOperation(state, action) {
@@ -1 +1 @@
1
- {"version":3,"file":"transitions.d.ts","sourceRoot":"","sources":["../../../../../document-models/invoice/src/reducers/transitions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,4DAA4D,CAAC;AA6B/G,eAAO,MAAM,4BAA4B,EAAE,4BA+J1C,CAAC"}
1
+ {"version":3,"file":"transitions.d.ts","sourceRoot":"","sources":["../../../../../document-models/invoice/src/reducers/transitions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,4DAA4D,CAAC;AA6B/G,eAAO,MAAM,4BAA4B,EAAE,4BAqK1C,CAAC"}
@@ -36,6 +36,10 @@ export const invoiceTransitionsOperations = {
36
36
  if (!action.input.invoiceNo || !action.input.dateIssued) {
37
37
  throw new Error("Invoice number and date issued are required");
38
38
  }
39
+ const wallet = state.issuer?.paymentRouting?.wallet;
40
+ if (!wallet?.address || (!wallet.chainName && !wallet.chainId)) {
41
+ throw new Error("Issuer wallet address and chain must be set before issuing an invoice");
42
+ }
39
43
  if (permittedTransitions[state.status].includes("ISSUED")) {
40
44
  state.status = "ISSUED";
41
45
  state.invoiceNo = action.input.invoiceNo;
@@ -1 +1 @@
1
- {"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/builder-team-admin/components/DriveExplorer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAclD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAuHtD"}
1
+ {"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/builder-team-admin/components/DriveExplorer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAuClD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAqMtD"}
@@ -1,13 +1,32 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { setName } from "document-model";
2
3
  import { FolderTree } from "./FolderTree.js";
3
4
  import { DriveContents } from "./DriveContents.js";
4
5
  import { ContributorsSection } from "./team-members.js";
5
- import { useDocumentsInSelectedDrive, showCreateDocumentModal, } from "@powerhousedao/reactor-browser";
6
- import { useMemo, useState } from "react";
6
+ import { useDocumentsInSelectedDrive, useSelectedDrive, addDocument, dispatchActions, setSelectedNode, } from "@powerhousedao/reactor-browser";
7
+ import { useCallback, useMemo, useState } from "react";
8
+ import { isValidName } from "document-drive";
7
9
  import { ExpenseReports } from "./expense-reports.js";
8
10
  import { SnapshotReports } from "./snapshot-reports.js";
9
11
  import { ResourcesServices } from "./ResourcesServices.js";
10
12
  import { ServiceSubscriptions } from "./service-subscriptions.js";
13
+ import { actions as builderProfileActions } from "@powerhousedao/builder-profile/document-models/builder-profile";
14
+ function generateCode(name) {
15
+ const words = name.trim().split(/\s+/).filter(Boolean);
16
+ if (words.length >= 2) {
17
+ // Acronym from first letter of each word, capped at 5
18
+ let code = words.map((w) => w[0]).join("");
19
+ // If only 2 words, pad with second letter of the first word
20
+ if (code.length < 3 && words[0].length > 1) {
21
+ code = words[0][0] + words[0][1] + words[1][0];
22
+ }
23
+ return code.slice(0, 5).toUpperCase();
24
+ }
25
+ // Single word: first, middle, last letter
26
+ const word = words[0];
27
+ const mid = Math.floor(word.length / 2);
28
+ return (word[0] + word[mid] + word[word.length - 1]).toUpperCase();
29
+ }
11
30
  /**
12
31
  * Main drive explorer component with sidebar navigation and content area.
13
32
  * Layout: Left sidebar (folder tree) + Right content area (files/folders + document editor)
@@ -17,15 +36,56 @@ export function DriveExplorer({ children }) {
17
36
  const showDocumentEditor = !!children;
18
37
  const documentsInSelectedDrive = useDocumentsInSelectedDrive();
19
38
  const [customView, setCustomView] = useState(null);
39
+ const [profileName, setProfileName] = useState("");
40
+ const [isCreating, setIsCreating] = useState(false);
41
+ const [selectedDrive] = useSelectedDrive();
20
42
  // Check if builder profile document exists
21
43
  const hasBuilderProfile = useMemo(() => {
22
44
  if (!documentsInSelectedDrive)
23
45
  return false;
24
46
  return documentsInSelectedDrive.some((doc) => doc.header.documentType === "powerhouse/builder-profile");
25
47
  }, [documentsInSelectedDrive]);
26
- // If no builder profile exists, show only the intro dialog
48
+ const handleCreateProfile = useCallback(async () => {
49
+ const trimmedName = profileName.trim();
50
+ const driveId = selectedDrive?.header.id;
51
+ if (!trimmedName || !driveId || isCreating)
52
+ return;
53
+ setIsCreating(true);
54
+ try {
55
+ const createdNode = await addDocument(driveId, trimmedName, "powerhouse/builder-profile");
56
+ if (!createdNode?.id) {
57
+ console.error("Failed to create builder profile document");
58
+ return;
59
+ }
60
+ // Set the profile name, slug, and code in the document state
61
+ const slug = trimmedName
62
+ .toLowerCase()
63
+ .replace(/[^a-z0-9]+/g, "-")
64
+ .replace(/^-|-$/g, "");
65
+ const code = generateCode(trimmedName);
66
+ await dispatchActions(builderProfileActions.updateProfile({ name: trimmedName, slug, code }), createdNode.id);
67
+ // Set the document name to match
68
+ await dispatchActions(setName(trimmedName), createdNode.id);
69
+ // Deselect so the main drive view renders instead of the document editor
70
+ setSelectedNode("");
71
+ }
72
+ catch (error) {
73
+ console.error("Error creating builder profile:", error);
74
+ }
75
+ finally {
76
+ setIsCreating(false);
77
+ }
78
+ }, [profileName, selectedDrive?.header.id, isCreating]);
79
+ const handleSubmit = useCallback((e) => {
80
+ e.preventDefault();
81
+ if (isValidName(profileName) && !isCreating) {
82
+ void handleCreateProfile();
83
+ }
84
+ }, [profileName, isCreating, handleCreateProfile]);
85
+ // If no builder profile exists, show the creation form
27
86
  if (!hasBuilderProfile) {
28
- return (_jsx("div", { className: "flex h-full items-center justify-center px-4 py-12", children: _jsxs("div", { className: "relative w-full max-w-2xl overflow-hidden rounded-2xl border border-slate-200/50 bg-gradient-to-br from-slate-50 via-blue-50/30 to-indigo-50/40 p-12 shadow-xl shadow-slate-200/50 backdrop-blur-sm", children: [_jsx("div", { className: "absolute -right-20 -top-20 h-64 w-64 rounded-full bg-gradient-to-br from-blue-400/20 to-indigo-400/20 blur-3xl" }), _jsx("div", { className: "absolute -bottom-16 -left-16 h-48 w-48 rounded-full bg-gradient-to-br from-indigo-300/20 to-purple-300/20 blur-2xl" }), _jsxs("div", { className: "relative z-10 text-center", children: [_jsx("div", { className: "mb-6 inline-flex items-center justify-center rounded-full bg-gradient-to-r from-blue-500 to-indigo-600 p-3 shadow-lg shadow-blue-500/30", children: _jsx("svg", { className: "h-8 w-8 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }) }), _jsx("h2", { className: "mb-4 text-3xl font-bold tracking-tight text-slate-900", children: "Create your Builder Team Profile" }), _jsx("p", { className: "mb-8 text-lg leading-relaxed text-slate-600", children: "Get started by creating your builder profile to manage your team, services, and build your presence in the Achra ecosystem." }), _jsxs("button", { className: "group relative overflow-hidden rounded-xl bg-gradient-to-r from-blue-600 to-indigo-600 px-8 py-4 text-base font-semibold text-white shadow-lg shadow-blue-500/40 transition-all duration-300 hover:scale-105 hover:shadow-xl hover:shadow-blue-500/50 active:scale-100", onClick: () => showCreateDocumentModal("powerhouse/builder-profile"), children: [_jsxs("span", { className: "relative z-10 flex items-center gap-2", children: [_jsx("span", { children: "Create Builder Profile" }), _jsx("svg", { className: "h-5 w-5 transition-transform duration-300 group-hover:translate-x-1", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 7l5 5m0 0l-5 5m5-5H6" }) })] }), _jsx("div", { className: "absolute inset-0 bg-gradient-to-r from-blue-700 to-indigo-700 opacity-0 transition-opacity duration-300 group-hover:opacity-100" })] })] })] }) }));
87
+ const isValid = isValidName(profileName);
88
+ return (_jsx("div", { className: "flex h-full items-center justify-center px-4 py-12", children: _jsxs("div", { className: "relative w-full max-w-2xl overflow-hidden rounded-2xl border border-slate-200/50 bg-gradient-to-br from-slate-50 via-blue-50/30 to-indigo-50/40 p-12 shadow-xl shadow-slate-200/50 backdrop-blur-sm", children: [_jsx("div", { className: "absolute -right-20 -top-20 h-64 w-64 rounded-full bg-gradient-to-br from-blue-400/20 to-indigo-400/20 blur-3xl" }), _jsx("div", { className: "absolute -bottom-16 -left-16 h-48 w-48 rounded-full bg-gradient-to-br from-indigo-300/20 to-purple-300/20 blur-2xl" }), _jsxs("div", { className: "relative z-10 text-center", children: [_jsx("div", { className: "mb-6 inline-flex items-center justify-center rounded-full bg-gradient-to-r from-blue-500 to-indigo-600 p-3 shadow-lg shadow-blue-500/30", children: _jsx("svg", { className: "h-8 w-8 text-white", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" }) }) }), _jsx("h2", { className: "mb-4 text-3xl font-bold tracking-tight text-slate-900", children: "Create your Builder Team Profile" }), _jsx("p", { className: "mb-8 text-lg leading-relaxed text-slate-600", children: "Get started by creating your builder profile to manage your team, services, and build your presence in the Achra ecosystem." }), _jsxs("form", { onSubmit: handleSubmit, className: "mx-auto max-w-md", children: [!isValid && profileName && (_jsx("div", { className: "mb-2 text-sm text-red-500", children: "Document name must be valid URL characters." })), _jsx("input", { type: "text", value: profileName, onChange: (e) => setProfileName(e.target.value), placeholder: "Builder Profile name", maxLength: 100, disabled: isCreating, className: "mb-6 w-full rounded-xl border border-slate-200 bg-white px-4 py-3 text-base text-slate-900 placeholder-slate-400 shadow-sm outline-none transition-all focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 disabled:opacity-50" }), _jsxs("button", { type: "submit", disabled: !isValid || isCreating, className: "group relative w-full overflow-hidden rounded-xl bg-gradient-to-r from-blue-600 to-indigo-600 px-8 py-4 text-base font-semibold text-white shadow-lg shadow-blue-500/40 transition-all duration-300 hover:scale-105 hover:shadow-xl hover:shadow-blue-500/50 active:scale-100 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:scale-100 disabled:hover:shadow-lg", children: [_jsxs("span", { className: "relative z-10 flex items-center justify-center gap-2", children: [_jsx("span", { children: isCreating ? "Creating..." : "Create Builder Profile" }), !isCreating && (_jsx("svg", { className: "h-5 w-5 transition-transform duration-300 group-hover:translate-x-1", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 7l5 5m0 0l-5 5m5-5H6" }) }))] }), _jsx("div", { className: "absolute inset-0 bg-gradient-to-r from-blue-700 to-indigo-700 opacity-0 transition-opacity duration-300 group-hover:opacity-100" })] })] })] })] }) }));
29
89
  }
30
90
  // Render the appropriate content based on state
31
91
  const renderContent = () => {
@@ -5,6 +5,6 @@ export const BuilderTeamAdmin = {
5
5
  documentTypes: ["powerhouse/document-drive"],
6
6
  config: {
7
7
  id: "builder-team-admin",
8
- name: "builder-team-admin",
8
+ name: "Builder Team Admin",
9
9
  },
10
10
  };
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardHome.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/DashboardHome.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAK1D,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,KAAK,IAAI,CAAC;CAClE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,cAAc,EAAE,EAAE,kBAAkB,2CA8WnE"}
1
+ {"version":3,"file":"DashboardHome.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/DashboardHome.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAK1D,UAAU,kBAAkB;IAC1B,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,KAAK,IAAI,CAAC;CAClE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,cAAc,EAAE,EAAE,kBAAkB,2CAqWnE"}
@@ -1,9 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Wallet, CheckCircle2, ArrowRight, Building2, User, } from "lucide-react";
3
- import { useCallback, useMemo, useState } from "react";
3
+ import { useCallback, useMemo } from "react";
4
4
  import { addDocument, useDocumentsInSelectedDrive, useSelectedDrive, setSelectedNode, isFileNodeKind, } from "@powerhousedao/reactor-browser";
5
5
  import { useBillingFolderStructure } from "../hooks/useBillingFolderStructure.js";
6
- import { CreateHubProfileModal } from "./CreateHubProfileModal.js";
7
6
  /**
8
7
  * Dashboard home page for the Operational Hub
9
8
  * Shows setup status and guides users through important actions
@@ -97,8 +96,6 @@ export function DashboardHome({ onFolderSelect }) {
97
96
  parts.push(statusParts.join(", "));
98
97
  return parts.join(" · ");
99
98
  }, [selectedDrive, documentsInDrive, monthFolders.size, paymentsFolderIds]);
100
- // State for custom create hub profile modal
101
- const [showCreateHubModal, setShowCreateHubModal] = useState(false);
102
99
  const handleOpenAccounts = useCallback(async () => {
103
100
  if (accountsDocument) {
104
101
  setSelectedNode(accountsDocument.header.id);
@@ -124,9 +121,6 @@ export function DashboardHome({ onFolderSelect }) {
124
121
  if (operationalHubProfileDocument) {
125
122
  setSelectedNode(operationalHubProfileDocument.header.id);
126
123
  }
127
- else {
128
- setShowCreateHubModal(true);
129
- }
130
124
  }, [operationalHubProfileDocument]);
131
125
  // Calculate setup progress with clickable steps
132
126
  const setupSteps = useMemo(() => {
@@ -165,5 +159,5 @@ export function DashboardHome({ onFolderSelect }) {
165
159
  ? profileStats || "View and update your hub profile"
166
160
  : "Set up your hub profile" })] })] }), _jsx(ArrowRight, { className: "w-5 h-5 text-gray-400 group-hover:text-blue-500 transition-colors" })] }), !operationalHubProfileDocument && (_jsx("div", { className: "mt-3 pt-3 border-t border-gray-100", children: _jsx("span", { className: "text-xs font-medium text-amber-600 bg-amber-50 px-2 py-1 rounded", children: "Action Required" }) }))] }), _jsxs("button", { onClick: () => void handleOpenAccounts(), className: "bg-white rounded-xl border border-gray-200 p-5 text-left hover:shadow-md hover:border-blue-200 transition-all group", children: [_jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: `p-3 rounded-lg ${accountsDocument ? "bg-green-100" : "bg-amber-100"}`, children: _jsx(Wallet, { className: `w-5 h-5 ${accountsDocument ? "text-green-600" : "text-amber-600"}` }) }), _jsxs("div", { children: [_jsx("h3", { className: "font-semibold text-gray-900", children: "Accounts" }), _jsx("p", { className: "text-sm text-gray-500", children: accountsDocument
167
161
  ? accountStats || "View and manage accounts"
168
- : "Set up your accounts first" })] })] }), _jsx(ArrowRight, { className: "w-5 h-5 text-gray-400 group-hover:text-blue-500 transition-colors" })] }), !accountsDocument && (_jsx("div", { className: "mt-3 pt-3 border-t border-gray-100", children: _jsx("span", { className: "text-xs font-medium text-amber-600 bg-amber-50 px-2 py-1 rounded", children: "Action Required" }) }))] }), _jsx("button", { onClick: handleOpenBilling, className: "bg-white rounded-xl border border-gray-200 p-5 text-left hover:shadow-md hover:border-blue-200 transition-all group", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "p-3 bg-blue-100 rounded-lg", children: _jsx(Building2, { className: "w-5 h-5 text-blue-600" }) }), _jsxs("div", { children: [_jsx("h3", { className: "font-semibold text-gray-900", children: "Billing" }), _jsx("p", { className: "text-sm text-gray-500", children: billingStats || "Manage monthly billing cycles" })] })] }), _jsx(ArrowRight, { className: "w-5 h-5 text-gray-400 group-hover:text-blue-500 transition-colors" })] }) })] }), _jsx(CreateHubProfileModal, { isOpen: showCreateHubModal, onClose: () => setShowCreateHubModal(false) })] }));
162
+ : "Set up your accounts first" })] })] }), _jsx(ArrowRight, { className: "w-5 h-5 text-gray-400 group-hover:text-blue-500 transition-colors" })] }), !accountsDocument && (_jsx("div", { className: "mt-3 pt-3 border-t border-gray-100", children: _jsx("span", { className: "text-xs font-medium text-amber-600 bg-amber-50 px-2 py-1 rounded", children: "Action Required" }) }))] }), _jsx("button", { onClick: handleOpenBilling, className: "bg-white rounded-xl border border-gray-200 p-5 text-left hover:shadow-md hover:border-blue-200 transition-all group", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "p-3 bg-blue-100 rounded-lg", children: _jsx(Building2, { className: "w-5 h-5 text-blue-600" }) }), _jsxs("div", { children: [_jsx("h3", { className: "font-semibold text-gray-900", children: "Billing" }), _jsx("p", { className: "text-sm text-gray-500", children: billingStats || "Manage monthly billing cycles" })] })] }), _jsx(ArrowRight, { className: "w-5 h-5 text-gray-400 group-hover:text-blue-500 transition-colors" })] }) })] })] }));
169
163
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/DriveExplorer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAQlD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CA4EtD"}
1
+ {"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/DriveExplorer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAkBlD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAqOtD"}
@@ -1,5 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useEffect, useRef } from "react";
2
+ import { setName } from "document-model";
3
+ import { useState, useEffect, useRef, useCallback, useMemo } from "react";
4
+ import { useDocumentsInSelectedDrive, useSelectedDrive, addDocument, dispatchActions, setSelectedNode, } from "@powerhousedao/reactor-browser";
5
+ import { isValidName } from "document-drive";
6
+ import { setOperationalHubName } from "../../../document-models/operational-hub-profile/gen/configuration/creators.js";
3
7
  import { ToastRenderer } from "./ToastRenderer.js";
4
8
  import { DriveContents } from "./DriveContents.js";
5
9
  import { FolderTree } from "./FolderTree.js";
@@ -12,6 +16,48 @@ import { DocumentDropZone } from "./DocumentDropZone.js";
12
16
  export function DriveExplorer({ children }) {
13
17
  // if a document is selected then its editor will be passed as children
14
18
  const showDocumentEditor = !!children;
19
+ const documentsInSelectedDrive = useDocumentsInSelectedDrive();
20
+ const [selectedDrive] = useSelectedDrive();
21
+ const [hubName, setHubName] = useState("");
22
+ const [isCreating, setIsCreating] = useState(false);
23
+ // Check if operational hub profile document exists
24
+ const hasHubProfile = useMemo(() => {
25
+ if (!documentsInSelectedDrive)
26
+ return false;
27
+ return documentsInSelectedDrive.some((doc) => doc.header.documentType === "powerhouse/operational-hub-profile");
28
+ }, [documentsInSelectedDrive]);
29
+ const handleCreateHubProfile = useCallback(async () => {
30
+ const trimmedName = hubName.trim();
31
+ const driveId = selectedDrive?.header.id;
32
+ if (!trimmedName || !driveId || isCreating)
33
+ return;
34
+ setIsCreating(true);
35
+ try {
36
+ const createdNode = await addDocument(driveId, trimmedName, "powerhouse/operational-hub-profile", undefined, undefined, undefined, "operational-hub-profile-editor");
37
+ if (!createdNode?.id) {
38
+ console.error("Failed to create operational hub profile document");
39
+ return;
40
+ }
41
+ // Set the hub name in the document state
42
+ await dispatchActions(setOperationalHubName({ name: trimmedName }), createdNode.id);
43
+ // Set the document name to match
44
+ await dispatchActions(setName(trimmedName), createdNode.id);
45
+ // Deselect so the main drive view renders instead of the document editor
46
+ setSelectedNode("");
47
+ }
48
+ catch (error) {
49
+ console.error("Error creating operational hub profile:", error);
50
+ }
51
+ finally {
52
+ setIsCreating(false);
53
+ }
54
+ }, [hubName, selectedDrive?.header.id, isCreating]);
55
+ const handleSubmit = useCallback((e) => {
56
+ e.preventDefault();
57
+ if (isValidName(hubName) && !isCreating) {
58
+ void handleCreateHubProfile();
59
+ }
60
+ }, [hubName, isCreating, handleCreateHubProfile]);
15
61
  // Track which folder is selected for content routing
16
62
  const [selectedFolder, setSelectedFolder] = useState(null);
17
63
  // Track active node in sidebar for visual selection sync
@@ -47,6 +93,11 @@ export function DriveExplorer({ children }) {
47
93
  setActiveNodeId(folderInfo.folderId);
48
94
  }
49
95
  };
96
+ // If no hub profile exists, show the creation form
97
+ if (!hasHubProfile) {
98
+ const isValid = isValidName(hubName);
99
+ return (_jsx("div", { className: "flex h-full items-center justify-center px-4 py-12", children: _jsxs("div", { className: "relative w-full max-w-2xl overflow-hidden rounded-2xl border border-slate-200/50 bg-gradient-to-br from-slate-50 via-purple-50/30 to-indigo-50/40 p-12 shadow-xl shadow-slate-200/50 backdrop-blur-sm", children: [_jsx("div", { className: "absolute -right-20 -top-20 h-64 w-64 rounded-full bg-gradient-to-br from-purple-400/20 to-indigo-400/20 blur-3xl" }), _jsx("div", { className: "absolute -bottom-16 -left-16 h-48 w-48 rounded-full bg-gradient-to-br from-indigo-300/20 to-purple-300/20 blur-2xl" }), _jsxs("div", { className: "relative z-10 text-center", children: [_jsx("div", { className: "mb-6 inline-flex items-center justify-center rounded-full bg-gradient-to-r from-purple-600 to-indigo-600 p-3 shadow-lg shadow-purple-500/30", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className: "text-white", children: _jsx("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }) }) }), _jsx("h2", { className: "mb-4 text-3xl font-bold tracking-tight text-slate-900", children: "Create your Operational Hub" }), _jsx("p", { className: "mb-8 text-lg leading-relaxed text-slate-600", children: "Get started by creating your operational hub to manage accounts, billing, and financial reporting." }), _jsxs("form", { onSubmit: handleSubmit, className: "mx-auto max-w-md", children: [!isValid && hubName && (_jsx("div", { className: "mb-2 text-sm text-red-500", children: "Document name must be valid URL characters." })), _jsx("input", { type: "text", value: hubName, onChange: (e) => setHubName(e.target.value), placeholder: "Operational Hub name", maxLength: 100, disabled: isCreating, className: "mb-6 w-full rounded-xl border border-slate-200 bg-white px-4 py-3 text-base text-slate-900 placeholder-slate-400 shadow-sm outline-none transition-all focus:border-purple-500 focus:ring-2 focus:ring-purple-500/20 disabled:opacity-50" }), _jsxs("button", { type: "submit", disabled: !isValid || isCreating, className: "group relative w-full overflow-hidden rounded-xl bg-gradient-to-r from-purple-600 to-indigo-600 px-8 py-4 text-base font-semibold text-white shadow-lg shadow-purple-500/40 transition-all duration-300 hover:scale-105 hover:shadow-xl hover:shadow-purple-500/50 active:scale-100 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:scale-100 disabled:hover:shadow-lg", children: [_jsxs("span", { className: "relative z-10 flex items-center justify-center gap-2", children: [_jsx("span", { children: isCreating ? "Creating..." : "Create Operational Hub" }), !isCreating && (_jsx("svg", { className: "h-5 w-5 transition-transform duration-300 group-hover:translate-x-1", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M13 7l5 5m0 0l-5 5m5-5H6" }) }))] }), _jsx("div", { className: "absolute inset-0 bg-gradient-to-r from-purple-700 to-indigo-700 opacity-0 transition-opacity duration-300 group-hover:opacity-100" })] })] })] })] }) }));
100
+ }
50
101
  return (_jsxs("div", { className: "flex h-full w-full overflow-hidden", children: [_jsx(FolderTreeErrorBoundary, { children: _jsx(FolderTree, { onFolderSelect: handleFolderSelect, activeNodeId: activeNodeId, onActiveNodeIdChange: setActiveNodeId }) }), _jsx(ToastRenderer, {}), _jsx(DocumentDropZone, { className: "flex-1 min-w-0 h-full overflow-x-hidden overflow-y-auto", children: showDocumentEditor ? (
51
102
  /* Document editor view */
52
103
  _jsx("div", { className: "min-h-full", children: children })) : (
@@ -1 +1 @@
1
- {"version":3,"file":"FolderTree.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/FolderTree.tsx"],"names":[],"mappings":"AA2CA,uCAAuC;AACvC,MAAM,MAAM,UAAU,GAClB,UAAU,GACV,WAAW,GACX,SAAS,GACT,eAAe,GACf,IAAI,CAAC;AAET,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,KAAK,eAAe,GAAG;IACrB,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,KAAK,IAAI,CAAC;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,cAAc,EACd,YAAY,EAAE,sBAAsB,EACpC,oBAAoB,GACrB,EAAE,eAAe,2CA4cjB"}
1
+ {"version":3,"file":"FolderTree.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/FolderTree.tsx"],"names":[],"mappings":"AA0CA,uCAAuC;AACvC,MAAM,MAAM,UAAU,GAClB,UAAU,GACV,WAAW,GACX,SAAS,GACT,eAAe,GACf,IAAI,CAAC;AAET,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,KAAK,eAAe,GAAG;IACrB,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,KAAK,IAAI,CAAC;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,cAAc,EACd,YAAY,EAAE,sBAAsB,EACpC,oBAAoB,GACrB,EAAE,eAAe,2CA8bjB"}
@@ -4,7 +4,6 @@ import { addDocument, setSelectedNode, useDocumentsInSelectedDrive, useSelectedD
4
4
  import { Wallet, FileText, Building2, Calendar, CreditCard, BarChart3, Camera, User, } from "lucide-react";
5
5
  import { useCallback, useMemo, useState } from "react";
6
6
  import { useBillingFolderStructure } from "../hooks/useBillingFolderStructure.js";
7
- import { CreateHubProfileModal } from "./CreateHubProfileModal.js";
8
7
  const ICON_SIZE = 16;
9
8
  const SUBSCRIPTIONS_FOLDER_NAME = "Subscriptions";
10
9
  /**
@@ -29,8 +28,6 @@ export function FolderTree({ onFolderSelect, activeNodeId: controlledActiveNodeI
29
28
  const [localActiveNodeId, setLocalActiveNodeId] = useState("");
30
29
  const activeNodeId = controlledActiveNodeId ?? localActiveNodeId;
31
30
  const setActiveNodeId = onActiveNodeIdChange ?? setLocalActiveNodeId;
32
- // State for custom create hub profile modal
33
- const [showCreateHubModal, setShowCreateHubModal] = useState(false);
34
31
  const documentsInDrive = useDocumentsInSelectedDrive();
35
32
  const [driveDocument] = useSelectedDrive();
36
33
  const { billingFolder, monthFolders, paymentsFolderIds, reportingFolderIds } = useBillingFolderStructure();
@@ -275,10 +272,6 @@ export function FolderTree({ onFolderSelect, activeNodeId: controlledActiveNodeI
275
272
  onFolderSelect?.(null);
276
273
  safeSetSelectedNode(operationalHubProfileDocument.header.id);
277
274
  }
278
- else {
279
- safeSetSelectedNode("");
280
- setShowCreateHubModal(true);
281
- }
282
275
  return;
283
276
  }
284
277
  // Check if this is an account-transactions child node
@@ -369,7 +362,7 @@ export function FolderTree({ onFolderSelect, activeNodeId: controlledActiveNodeI
369
362
  // Use a stable key based on the drive ID only
370
363
  // Previously this changed on every folder/document add, causing sidebar to remount and lose collapsed state
371
364
  const sidebarKey = driveDocument?.header.id || "empty";
372
- return (_jsxs(_Fragment, { children: [_jsxs(SidebarProvider, { nodes: navigationSections, children: [_jsx("style", { children: `
365
+ return (_jsx(_Fragment, { children: _jsxs(SidebarProvider, { nodes: navigationSections, children: [_jsx("style", { children: `
373
366
  .folder-tree-sidebar .sidebar__item-caret--no-children {
374
367
  visibility: hidden;
375
368
  }
@@ -377,11 +370,9 @@ export function FolderTree({ onFolderSelect, activeNodeId: controlledActiveNodeI
377
370
  display: none;
378
371
  }
379
372
  ` }), _jsx(Sidebar, { className: "pt-1 folder-tree-sidebar", nodes: navigationSections, activeNodeId: sanitizedActiveNodeId, onActiveNodeChange: handleActiveNodeChange, sidebarTitle: operationalHubProfileDocument?.state?.global?.name ||
380
- "Operational Hub", showSearchBar: false, resizable: true, allowPinning: false, showStatusFilter: false, initialWidth: 256, defaultLevel: 2, handleOnTitleClick: () => {
381
- onFolderSelect?.(null);
382
- safeSetSelectedNode("");
383
- setActiveNodeId("");
384
- } }, sidebarKey)] }), _jsx(CreateHubProfileModal, { isOpen: showCreateHubModal, onClose: () => setShowCreateHubModal(false), onCreated: () => {
385
- onFolderSelect?.(null);
386
- } })] }));
373
+ "Operational Hub", showSearchBar: false, resizable: true, allowPinning: false, showStatusFilter: false, initialWidth: 256, defaultLevel: 2, handleOnTitleClick: () => {
374
+ onFolderSelect?.(null);
375
+ safeSetSelectedNode("");
376
+ setActiveNodeId("");
377
+ } }, sidebarKey)] }) }));
387
378
  }
@@ -1,13 +1,20 @@
1
1
  import type { MonthReportSet } from "../hooks/useMonthlyReports.js";
2
+ export interface MonthPaymentStats {
3
+ totalInvoices: number;
4
+ pendingCount: number;
5
+ paidCount: number;
6
+ }
2
7
  interface MonthReportCardProps {
3
8
  reportSet: MonthReportSet;
4
9
  defaultExpanded?: boolean;
5
10
  onCreateExpenseReport?: (monthName: string, folderId: string) => void;
6
11
  onCreateSnapshotReport?: (monthName: string, folderId: string) => void;
12
+ onViewPayments?: (monthName: string) => void;
13
+ paymentStats?: MonthPaymentStats;
7
14
  }
8
15
  /**
9
16
  * Collapsible month card showing all reports for a month
10
17
  */
11
- export declare function MonthReportCard({ reportSet, defaultExpanded, onCreateExpenseReport, onCreateSnapshotReport, }: MonthReportCardProps): import("react/jsx-runtime").JSX.Element;
18
+ export declare function MonthReportCard({ reportSet, defaultExpanded, onCreateExpenseReport, onCreateSnapshotReport, onViewPayments, paymentStats, }: MonthReportCardProps): import("react/jsx-runtime").JSX.Element;
12
19
  export {};
13
20
  //# sourceMappingURL=MonthReportCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MonthReportCard.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/MonthReportCard.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,+BAA+B,CAAC;AAEvC,UAAU,oBAAoB;IAC5B,SAAS,EAAE,cAAc,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtE,sBAAsB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACxE;AA6ED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,eAAuB,EACvB,qBAAqB,EACrB,sBAAsB,GACvB,EAAE,oBAAoB,2CA2GtB"}
1
+ {"version":3,"file":"MonthReportCard.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/MonthReportCard.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,oBAAoB;IAC5B,SAAS,EAAE,cAAc,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,qBAAqB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtE,sBAAsB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvE,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,YAAY,CAAC,EAAE,iBAAiB,CAAC;CAClC;AA6ED;;GAEG;AACH,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,eAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,YAAY,GACb,EAAE,oBAAoB,2CAiJtB"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useCallback } from "react";
3
- import { Calendar, ChevronDown, ChevronRight, Camera, FileText, ArrowRight, Plus, } from "lucide-react";
3
+ import { Calendar, ChevronDown, ChevronRight, Camera, CreditCard, FileText, ArrowRight, Plus, } from "lucide-react";
4
4
  import { setSelectedNode } from "@powerhousedao/reactor-browser";
5
5
  /**
6
6
  * Get color classes for status badges
@@ -47,7 +47,7 @@ function ReportRow({ report, isSnapshot = false, }) {
47
47
  /**
48
48
  * Collapsible month card showing all reports for a month
49
49
  */
50
- export function MonthReportCard({ reportSet, defaultExpanded = false, onCreateExpenseReport, onCreateSnapshotReport, }) {
50
+ export function MonthReportCard({ reportSet, defaultExpanded = false, onCreateExpenseReport, onCreateSnapshotReport, onViewPayments, paymentStats, }) {
51
51
  const [isExpanded, setIsExpanded] = useState(defaultExpanded);
52
52
  const overallColors = getStatusColors(reportSet.overallStatus);
53
53
  const toggleExpanded = useCallback(() => {
@@ -67,9 +67,12 @@ export function MonthReportCard({ reportSet, defaultExpanded = false, onCreateEx
67
67
  reportSet.monthName,
68
68
  reportSet.reportingFolderId,
69
69
  ]);
70
+ const handleViewPayments = useCallback(() => {
71
+ onViewPayments?.(reportSet.monthName);
72
+ }, [onViewPayments, reportSet.monthName]);
70
73
  const reportCountText = reportSet.reportCount === 1
71
74
  ? "1 Report"
72
75
  : `${reportSet.reportCount} Reports`;
73
- return (_jsxs("div", { className: "bg-white rounded-xl border border-gray-200 overflow-hidden", children: [_jsxs("button", { onClick: toggleExpanded, className: "w-full flex items-center justify-between p-4 hover:bg-gray-50 transition-colors text-left", children: [_jsxs("div", { className: "flex items-center gap-3", children: [isExpanded ? (_jsx(ChevronDown, { className: "w-5 h-5 text-gray-400" })) : (_jsx(ChevronRight, { className: "w-5 h-5 text-gray-400" })), _jsx(Calendar, { className: "w-5 h-5 text-gray-600" }), _jsx("span", { className: "font-medium text-gray-900", children: reportSet.monthName })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: "text-sm text-gray-500", children: reportCountText }), reportSet.reportCount > 0 && (_jsx("span", { className: `px-2 py-0.5 text-xs font-medium rounded-full ${overallColors.bg} ${overallColors.text}`, children: getStatusLabel(reportSet.overallStatus) }))] })] }), isExpanded && (_jsxs("div", { className: "border-t border-gray-200", children: [reportSet.snapshotReport && (_jsx(ReportRow, { report: reportSet.snapshotReport, isSnapshot: true })), reportSet.expenseReports.map((report) => (_jsx(ReportRow, { report: report }, report.id))), reportSet.reportCount === 0 && (_jsx("div", { className: "p-4 text-center text-sm text-gray-500", children: "No reports created yet" })), (onCreateExpenseReport || onCreateSnapshotReport) &&
76
+ return (_jsxs("div", { className: "bg-white rounded-xl border border-gray-200 overflow-hidden", children: [_jsxs("button", { onClick: toggleExpanded, className: "w-full flex items-center justify-between p-4 hover:bg-gray-50 transition-colors text-left", children: [_jsxs("div", { className: "flex items-center gap-3", children: [isExpanded ? (_jsx(ChevronDown, { className: "w-5 h-5 text-gray-400" })) : (_jsx(ChevronRight, { className: "w-5 h-5 text-gray-400" })), _jsx(Calendar, { className: "w-5 h-5 text-gray-600" }), _jsx("span", { className: "font-medium text-gray-900", children: reportSet.monthName })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: "text-sm text-gray-500", children: reportCountText }), reportSet.reportCount > 0 && (_jsx("span", { className: `px-2 py-0.5 text-xs font-medium rounded-full ${overallColors.bg} ${overallColors.text}`, children: getStatusLabel(reportSet.overallStatus) }))] })] }), isExpanded && (_jsxs("div", { className: "border-t border-gray-200", children: [onViewPayments && (_jsxs("button", { onClick: handleViewPayments, className: "w-full flex items-center justify-between p-3 hover:bg-gray-50 cursor-pointer transition-colors border-b border-gray-100", children: [_jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [_jsx(CreditCard, { className: "w-4 h-4 text-emerald-500 flex-shrink-0" }), _jsx("span", { className: "text-sm font-medium text-gray-900", children: "Payments" }), paymentStats && paymentStats.totalInvoices > 0 && (_jsxs("span", { className: "text-xs text-gray-500", children: [paymentStats.totalInvoices, " invoice", paymentStats.totalInvoices !== 1 ? "s" : "", paymentStats.pendingCount > 0 && (_jsxs("span", { className: "text-amber-600", children: [" ", "\u00B7 ", paymentStats.pendingCount, " pending"] })), paymentStats.paidCount > 0 && (_jsxs("span", { className: "text-green-600", children: [" ", "\u00B7 ", paymentStats.paidCount, " paid"] }))] }))] }), _jsx(ArrowRight, { className: "w-4 h-4 text-gray-400 flex-shrink-0" })] })), reportSet.snapshotReport && (_jsx(ReportRow, { report: reportSet.snapshotReport, isSnapshot: true })), reportSet.expenseReports.map((report) => (_jsx(ReportRow, { report: report }, report.id))), reportSet.reportCount === 0 && (_jsx("div", { className: "p-4 text-center text-sm text-gray-500", children: "No reports created yet" })), (onCreateExpenseReport || onCreateSnapshotReport) &&
74
77
  reportSet.reportingFolderId && (_jsxs("div", { className: "p-3 border-t border-gray-100 bg-gray-50 flex items-center gap-3", children: [onCreateSnapshotReport && !reportSet.snapshotReport && (_jsxs("button", { onClick: handleCreateSnapshotReport, className: "flex items-center gap-2 text-sm text-purple-600 hover:text-purple-700 font-medium transition-colors", children: [_jsx(Plus, { className: "w-4 h-4" }), "Add Snapshot Report"] })), onCreateExpenseReport && (_jsxs("button", { onClick: handleCreateExpenseReport, className: "flex items-center gap-2 text-sm text-blue-600 hover:text-blue-700 font-medium transition-colors", children: [_jsx(Plus, { className: "w-4 h-4" }), "Add Expense Report"] }))] }))] }))] }));
75
78
  }
@@ -10,6 +10,6 @@ interface MonthlyReportsOverviewProps {
10
10
  * Monthly Reports Overview component for the billing page
11
11
  * Shows collapsible month cards with reports and status
12
12
  */
13
- export declare function MonthlyReportsOverview({ monthFolders, onCreateMonth, onActiveNodeIdChange, }: MonthlyReportsOverviewProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function MonthlyReportsOverview({ onFolderSelect, monthFolders, onCreateMonth, onActiveNodeIdChange, }: MonthlyReportsOverviewProps): import("react/jsx-runtime").JSX.Element;
14
14
  export {};
15
15
  //# sourceMappingURL=MonthlyReportsOverview.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MonthlyReportsOverview.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/MonthlyReportsOverview.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAa7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,UAAU,2BAA2B;IACnC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,KAAK,IAAI,CAAC;IACjE,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,oBAAoB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAsCD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,YAAY,EACZ,aAAa,EACb,oBAAoB,GACrB,EAAE,2BAA2B,2CA4U7B"}
1
+ {"version":3,"file":"MonthlyReportsOverview.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/MonthlyReportsOverview.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAe7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,UAAU,2BAA2B;IACnC,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,KAAK,IAAI,CAAC;IACjE,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,oBAAoB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAsCD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,cAAc,EACd,YAAY,EACZ,aAAa,EACb,oBAAoB,GACrB,EAAE,2BAA2B,2CAuZ7B"}
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useState, useMemo, useRef, useEffect } from "react";
3
3
  import { createPortal } from "react-dom";
4
4
  import { BarChart3, Plus, ChevronDown } from "lucide-react";
5
- import { useSelectedDrive, addDocument, dispatchActions, setSelectedNode, } from "@powerhousedao/reactor-browser";
5
+ import { useSelectedDrive, useDocumentsInSelectedDrive, addDocument, dispatchActions, setSelectedNode, isFileNodeKind, } from "@powerhousedao/reactor-browser";
6
6
  import { setName } from "document-model";
7
7
  import { moveNode } from "document-drive";
8
8
  import { useMonthlyReports } from "../hooks/useMonthlyReports.js";
@@ -42,14 +42,67 @@ function parseMonthDates(monthName) {
42
42
  * Monthly Reports Overview component for the billing page
43
43
  * Shows collapsible month cards with reports and status
44
44
  */
45
- export function MonthlyReportsOverview({ monthFolders, onCreateMonth, onActiveNodeIdChange, }) {
45
+ export function MonthlyReportsOverview({ onFolderSelect, monthFolders, onCreateMonth, onActiveNodeIdChange, }) {
46
46
  const { monthReportSets, isLoading } = useMonthlyReports();
47
47
  const [selectedDrive] = useSelectedDrive();
48
+ const documentsInDrive = useDocumentsInSelectedDrive();
48
49
  const [isCreating, setIsCreating] = useState(false);
49
50
  const [isAddingMonth, setIsAddingMonth] = useState(false);
50
51
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
51
52
  const dropdownRef = useRef(null);
52
53
  const driveId = selectedDrive?.header.id;
54
+ // Per-month payment stats for the Payments row in each card
55
+ const monthPaymentStatsMap = useMemo(() => {
56
+ const map = new Map();
57
+ if (!selectedDrive || !documentsInDrive || !monthFolders)
58
+ return map;
59
+ const nodes = selectedDrive.state.global.nodes;
60
+ for (const [monthName, folderInfo] of monthFolders.entries()) {
61
+ const paymentsFolderId = folderInfo.paymentsFolder?.id;
62
+ if (!paymentsFolderId)
63
+ continue;
64
+ const invoiceIds = new Set(nodes
65
+ .filter((n) => isFileNodeKind(n) &&
66
+ n.parentFolder === paymentsFolderId &&
67
+ n.documentType === "powerhouse/invoice")
68
+ .map((n) => n.id));
69
+ const invoices = documentsInDrive.filter((doc) => doc.header.documentType === "powerhouse/invoice" &&
70
+ invoiceIds.has(doc.header.id));
71
+ let pendingCount = 0;
72
+ let paidCount = 0;
73
+ for (const invoice of invoices) {
74
+ const status = (invoice.state.global?.status ??
75
+ "").toUpperCase();
76
+ if (status === "PAYMENTSENT" ||
77
+ status === "PAYMENTRECEIVED" ||
78
+ status === "PAYMENTCLOSED") {
79
+ paidCount++;
80
+ }
81
+ else if (status !== "REJECTED" && status !== "CANCELLED") {
82
+ pendingCount++;
83
+ }
84
+ }
85
+ map.set(monthName, {
86
+ totalInvoices: invoices.length,
87
+ pendingCount,
88
+ paidCount,
89
+ });
90
+ }
91
+ return map;
92
+ }, [selectedDrive, documentsInDrive, monthFolders]);
93
+ const handleViewPayments = useCallback((monthName) => {
94
+ if (!onFolderSelect || !monthFolders)
95
+ return;
96
+ const info = monthFolders.get(monthName);
97
+ if (!info?.paymentsFolder)
98
+ return;
99
+ onFolderSelect({
100
+ folderId: info.paymentsFolder.id,
101
+ folderType: "payments",
102
+ monthName,
103
+ reportingFolderId: info.reportingFolder?.id,
104
+ });
105
+ }, [onFolderSelect, monthFolders]);
53
106
  const buttonRef = useRef(null);
54
107
  const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0 });
55
108
  // Update dropdown position when opening
@@ -200,5 +253,5 @@ export function MonthlyReportsOverview({ monthFolders, onCreateMonth, onActiveNo
200
253
  if (monthReportSets.length === 0) {
201
254
  return (_jsxs("div", { className: "bg-white rounded-xl border border-gray-200 p-6 overflow-visible", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 mb-5", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "p-2 bg-indigo-100 rounded-lg", children: _jsx(BarChart3, { className: "w-5 h-5 text-indigo-600" }) }), _jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Monthly Reports" }), _jsx("p", { className: "text-sm text-gray-600", children: "Quick access to expense and snapshot reports" })] })] }), addMonthButton] }), _jsx("p", { className: "text-gray-500 text-sm text-center py-4", children: "No months configured yet. Click \"Add Month\" to get started." })] }));
202
255
  }
203
- return (_jsxs("div", { className: "bg-white rounded-xl border border-gray-200 p-6 overflow-visible", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 mb-5", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "p-2 bg-indigo-100 rounded-lg", children: _jsx(BarChart3, { className: "w-5 h-5 text-indigo-600" }) }), _jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Monthly Reports" }), _jsx("p", { className: "text-sm text-gray-600", children: "Quick access to expense and snapshot reports" })] })] }), addMonthButton] }), _jsx("div", { className: "space-y-3", children: monthReportSets.map((reportSet, index) => (_jsx(MonthReportCard, { reportSet: reportSet, defaultExpanded: index === 0, onCreateExpenseReport: handleCreateExpenseReport, onCreateSnapshotReport: handleCreateSnapshotReport }, reportSet.monthName))) })] }));
256
+ return (_jsxs("div", { className: "bg-white rounded-xl border border-gray-200 p-6 overflow-visible", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 mb-5", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "p-2 bg-indigo-100 rounded-lg", children: _jsx(BarChart3, { className: "w-5 h-5 text-indigo-600" }) }), _jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Monthly Reports" }), _jsx("p", { className: "text-sm text-gray-600", children: "Quick access to expense and snapshot reports" })] })] }), addMonthButton] }), _jsx("div", { className: "space-y-3", children: monthReportSets.map((reportSet, index) => (_jsx(MonthReportCard, { reportSet: reportSet, defaultExpanded: index === 0, onCreateExpenseReport: handleCreateExpenseReport, onCreateSnapshotReport: handleCreateSnapshotReport, onViewPayments: onFolderSelect ? handleViewPayments : undefined, paymentStats: monthPaymentStatsMap.get(reportSet.monthName) }, reportSet.monthName))) })] }));
204
257
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cbToast.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/cbToast.ts"],"names":[],"mappings":"AAEA,KAAK,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAE1D,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,CAAC;CACjB;AAcD,iBAAS,WAAW,CAAC,EAAE,EAAE,MAAM,QAG9B;AAED,wBAAgB,OAAO,CACrB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,QAM/B;AAED,wBAAgB,SAAS;;;EAWxB"}
1
+ {"version":3,"file":"cbToast.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/components/cbToast.ts"],"names":[],"mappings":"AAEA,KAAK,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAE1D,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,CAAC;CACjB;AAcD,iBAAS,WAAW,CAAC,EAAE,EAAE,MAAM,QAG9B;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE,QAKtE;AAED,wBAAgB,SAAS;;;EAWxB"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDocumentAutoPlacement.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/hooks/useDocumentAutoPlacement.ts"],"names":[],"mappings":"AAiBA,UAAU,8BAA8B;IACtC,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,IAAI,8BAA8B,CAiKzE"}
1
+ {"version":3,"file":"useDocumentAutoPlacement.d.ts","sourceRoot":"","sources":["../../../../editors/contributor-billing/hooks/useDocumentAutoPlacement.ts"],"names":[],"mappings":"AAiBA,UAAU,8BAA8B;IACtC,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,IAAI,8BAA8B,CAmNzE"}