@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.
- package/dist/document-models/invoice/src/reducers/general.d.ts.map +1 -1
- package/dist/document-models/invoice/src/reducers/general.js +8 -0
- package/dist/document-models/invoice/src/reducers/transitions.d.ts.map +1 -1
- package/dist/document-models/invoice/src/reducers/transitions.js +4 -0
- package/dist/editors/builder-team-admin/components/DriveExplorer.d.ts.map +1 -1
- package/dist/editors/builder-team-admin/components/DriveExplorer.js +64 -4
- package/dist/editors/builder-team-admin/module.js +1 -1
- package/dist/editors/contributor-billing/components/DashboardHome.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/DashboardHome.js +2 -8
- package/dist/editors/contributor-billing/components/DriveExplorer.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/DriveExplorer.js +52 -1
- package/dist/editors/contributor-billing/components/FolderTree.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/FolderTree.js +6 -15
- package/dist/editors/contributor-billing/components/MonthReportCard.d.ts +8 -1
- package/dist/editors/contributor-billing/components/MonthReportCard.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/MonthReportCard.js +6 -3
- package/dist/editors/contributor-billing/components/MonthlyReportsOverview.d.ts +1 -1
- package/dist/editors/contributor-billing/components/MonthlyReportsOverview.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/MonthlyReportsOverview.js +56 -3
- package/dist/editors/contributor-billing/components/cbToast.d.ts.map +1 -1
- package/dist/editors/contributor-billing/hooks/useDocumentAutoPlacement.d.ts.map +1 -1
- package/dist/editors/contributor-billing/hooks/useDocumentAutoPlacement.js +42 -2
- package/dist/editors/contributor-billing/module.js +1 -1
- package/dist/editors/invoice/editor.d.ts.map +1 -1
- package/dist/editors/invoice/editor.js +3 -2
- package/dist/editors/invoice/legalEntity/legalEntity.d.ts +2 -1
- package/dist/editors/invoice/legalEntity/legalEntity.d.ts.map +1 -1
- package/dist/editors/invoice/legalEntity/legalEntity.js +2 -2
- package/dist/editors/invoice/legalEntity/walletSection.d.ts +1 -0
- package/dist/editors/invoice/legalEntity/walletSection.d.ts.map +1 -1
- package/dist/editors/invoice/legalEntity/walletSection.js +2 -2
- package/dist/editors/invoice/validation/validationHandler.d.ts +1 -1
- package/dist/editors/invoice/validation/validationHandler.d.ts.map +1 -1
- package/dist/editors/invoice/validation/validationHandler.js +14 -1
- package/dist/editors/invoice/validation/validationManager.d.ts.map +1 -1
- package/dist/editors/invoice/validation/validationManager.js +3 -1
- package/dist/editors/invoice/validation/validationRules.d.ts +2 -0
- package/dist/editors/invoice/validation/validationRules.d.ts.map +1 -1
- package/dist/editors/invoice/validation/validationRules.js +50 -0
- package/dist/style.css +96 -3
- package/dist/subgraphs/budget-statements/resolvers.d.ts +38 -0
- package/dist/subgraphs/budget-statements/resolvers.d.ts.map +1 -1
- package/dist/subgraphs/budget-statements/resolvers.js +151 -44
- package/dist/subgraphs/budget-statements/resolvers.test.d.ts +2 -0
- package/dist/subgraphs/budget-statements/resolvers.test.d.ts.map +1 -0
- package/dist/subgraphs/budget-statements/resolvers.test.js +329 -0
- package/dist/subgraphs/budget-statements/schema.d.ts.map +1 -1
- package/dist/subgraphs/budget-statements/schema.js +8 -0
- package/package.json +16 -16
- package/dist/editors/contributor-billing/components/CreateHubProfileModal.d.ts +0 -12
- package/dist/editors/contributor-billing/components/CreateHubProfileModal.d.ts.map +0 -1
- 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,
|
|
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,
|
|
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;
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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 = () => {
|
|
@@ -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,
|
|
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
|
|
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" })] }) })] })
|
|
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;
|
|
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 {
|
|
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":"
|
|
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 (
|
|
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
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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":"
|
|
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;
|
|
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,
|
|
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,
|
|
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"}
|