@powerhousedao/contributor-billing 1.0.0-dev.19 → 1.0.0-dev.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/document-models/account-transactions/v1/module.d.ts +1 -1
- package/dist/document-models/account-transactions/v1/module.d.ts.map +1 -1
- package/dist/document-models/account-transactions/v1/module.js +4 -1
- package/dist/document-models/accounts/v1/module.d.ts +1 -1
- package/dist/document-models/accounts/v1/module.d.ts.map +1 -1
- package/dist/document-models/accounts/v1/module.js +4 -1
- package/dist/document-models/billing-statement/v1/module.d.ts +1 -1
- package/dist/document-models/billing-statement/v1/module.d.ts.map +1 -1
- package/dist/document-models/billing-statement/v1/module.js +4 -1
- package/dist/document-models/expense-report/v1/module.d.ts +1 -1
- package/dist/document-models/expense-report/v1/module.d.ts.map +1 -1
- package/dist/document-models/expense-report/v1/module.js +4 -1
- package/dist/document-models/invoice/v1/module.d.ts +1 -1
- package/dist/document-models/invoice/v1/module.d.ts.map +1 -1
- package/dist/document-models/invoice/v1/module.js +4 -1
- package/dist/document-models/operational-hub-profile/v1/module.d.ts +1 -1
- package/dist/document-models/operational-hub-profile/v1/module.d.ts.map +1 -1
- package/dist/document-models/operational-hub-profile/v1/module.js +4 -1
- package/dist/document-models/snapshot-report/v1/module.d.ts +1 -1
- package/dist/document-models/snapshot-report/v1/module.d.ts.map +1 -1
- package/dist/document-models/snapshot-report/v1/module.js +4 -1
- package/dist/editors/builder-team-admin/components/FolderTree.d.ts.map +1 -1
- package/dist/editors/builder-team-admin/components/FolderTree.js +8 -6
- package/dist/editors/contributor-billing/components/BillingOverview.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/BillingOverview.js +63 -3
- package/dist/editors/contributor-billing/components/DocumentDropZone.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/DocumentDropZone.js +10 -1
- package/dist/editors/contributor-billing/components/EmptyState.d.ts +7 -2
- package/dist/editors/contributor-billing/components/EmptyState.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/EmptyState.js +3 -8
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/HeaderStats.js +6 -0
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.d.ts.map +1 -1
- package/dist/editors/contributor-billing/components/InvoiceTable/InvoiceTable.js +20 -1
- package/dist/editors/contributor-billing/hooks/useDocumentAutoPlacement.d.ts.map +1 -1
- package/dist/editors/contributor-billing/hooks/useDocumentAutoPlacement.js +199 -1
- package/dist/style.css +1464 -151
- package/package.json +27 -20
|
@@ -2,6 +2,7 @@ import { useEffect, useMemo } from "react";
|
|
|
2
2
|
import { isFileNodeKind, isFolderNodeKind, useSelectedDrive, useDocumentsInSelectedDrive, useNodeActions, } from "@powerhousedao/reactor-browser";
|
|
3
3
|
import { isDocumentSynced } from "../../shared/document-sync.js";
|
|
4
4
|
import { useBillingFolderStructure } from "./useBillingFolderStructure.js";
|
|
5
|
+
import { cbToast } from "../components/cbToast.js";
|
|
5
6
|
// Module-level tracking to prevent duplicate processing
|
|
6
7
|
const globalProcessingState = {
|
|
7
8
|
processedDocs: new Map(), // driveId -> Set of doc IDs processed
|
|
@@ -20,7 +21,7 @@ const globalProcessingState = {
|
|
|
20
21
|
export function useDocumentAutoPlacement() {
|
|
21
22
|
const [driveDocument] = useSelectedDrive();
|
|
22
23
|
const documentsInDrive = useDocumentsInSelectedDrive();
|
|
23
|
-
const { reportingFolderIds, monthFolders, billingFolder, createMonthFolder } = useBillingFolderStructure();
|
|
24
|
+
const { reportingFolderIds, paymentsFolderIds, monthFolders, billingFolder, createMonthFolder, } = useBillingFolderStructure();
|
|
24
25
|
const { onMoveNode, onRenameNode } = useNodeActions();
|
|
25
26
|
const driveId = driveDocument?.header.id;
|
|
26
27
|
// Initialize module-level tracking for this drive
|
|
@@ -184,6 +185,203 @@ export function useDocumentAutoPlacement() {
|
|
|
184
185
|
onMoveNode,
|
|
185
186
|
onRenameNode,
|
|
186
187
|
]);
|
|
188
|
+
// Auto-place invoices into appropriate Payments folders based on dateIssued
|
|
189
|
+
useEffect(() => {
|
|
190
|
+
if (!driveId || !driveDocument || !documentsInDrive)
|
|
191
|
+
return;
|
|
192
|
+
const allNodes = driveDocument.state.global.nodes;
|
|
193
|
+
const processedDocs = globalProcessingState.processedDocs.get(driveId);
|
|
194
|
+
if (!processedDocs)
|
|
195
|
+
return;
|
|
196
|
+
// Find invoice file nodes that are NOT already in a Payments folder
|
|
197
|
+
const invoiceNodesToProcess = allNodes.filter((node) => isFileNodeKind(node) &&
|
|
198
|
+
node.documentType === "powerhouse/invoice" &&
|
|
199
|
+
!paymentsFolderIds.has(node.parentFolder || ""));
|
|
200
|
+
for (const fileNode of invoiceNodesToProcess) {
|
|
201
|
+
if (processedDocs.has(fileNode.id))
|
|
202
|
+
continue;
|
|
203
|
+
const doc = documentsInDrive.find((d) => d.header.documentType === "powerhouse/invoice" &&
|
|
204
|
+
d.header.id === fileNode.id);
|
|
205
|
+
if (!doc)
|
|
206
|
+
continue;
|
|
207
|
+
const dateIssued = doc.state.global.dateIssued;
|
|
208
|
+
const monthName = getMonthNameFromPeriod(dateIssued);
|
|
209
|
+
processedDocs.add(fileNode.id);
|
|
210
|
+
if (monthName) {
|
|
211
|
+
const monthInfo = monthFolders.get(monthName);
|
|
212
|
+
const paymentsFolder = monthInfo?.paymentsFolder;
|
|
213
|
+
if (paymentsFolder) {
|
|
214
|
+
console.log(`[DocumentAutoPlacement] Moving invoice ${fileNode.id} ("${fileNode.name}") to Payments folder for ${monthName}`);
|
|
215
|
+
onMoveNode(fileNode, paymentsFolder)
|
|
216
|
+
.then(() => {
|
|
217
|
+
console.log(`[DocumentAutoPlacement] Successfully moved invoice to ${monthName} > Payments`);
|
|
218
|
+
cbToast(`Invoice "${fileNode.name}" placed in ${monthName} > Payments`, { type: "success" });
|
|
219
|
+
})
|
|
220
|
+
.catch((error) => {
|
|
221
|
+
console.error(`[DocumentAutoPlacement] Failed to move invoice to Payments folder:`, error);
|
|
222
|
+
processedDocs.delete(fileNode.id);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
// Month folder doesn't exist — create it, then retry on next effect run
|
|
227
|
+
console.log(`[DocumentAutoPlacement] Month folder "${monthName}" doesn't exist for invoice, creating it`);
|
|
228
|
+
if (billingFolder && driveId) {
|
|
229
|
+
createMonthFolder(monthName)
|
|
230
|
+
.then(() => {
|
|
231
|
+
console.log(`[DocumentAutoPlacement] Created month folder "${monthName}" for invoice, will retry placement`);
|
|
232
|
+
processedDocs.delete(fileNode.id);
|
|
233
|
+
})
|
|
234
|
+
.catch((error) => {
|
|
235
|
+
console.error(`[DocumentAutoPlacement] Failed to create month folder "${monthName}" for invoice:`, error);
|
|
236
|
+
processedDocs.delete(fileNode.id);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
processedDocs.delete(fileNode.id);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
console.warn(`[DocumentAutoPlacement] Invoice ${fileNode.id} ("${fileNode.name}") has no dateIssued, leaving at root`);
|
|
246
|
+
cbToast(`Invoice "${fileNode.name}" has no issue date — could not auto-categorize. It remains at the drive root.`, { type: "warning" });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}, [
|
|
250
|
+
driveId,
|
|
251
|
+
driveDocument,
|
|
252
|
+
documentsInDrive,
|
|
253
|
+
paymentsFolderIds,
|
|
254
|
+
monthFolders,
|
|
255
|
+
billingFolder,
|
|
256
|
+
createMonthFolder,
|
|
257
|
+
onMoveNode,
|
|
258
|
+
]);
|
|
259
|
+
// Auto-place snapshot reports into appropriate Reporting folders based on reportPeriodStart
|
|
260
|
+
useEffect(() => {
|
|
261
|
+
if (!driveId || !driveDocument || !documentsInDrive)
|
|
262
|
+
return;
|
|
263
|
+
const allNodes = driveDocument.state.global.nodes;
|
|
264
|
+
const processedDocs = globalProcessingState.processedDocs.get(driveId);
|
|
265
|
+
if (!processedDocs)
|
|
266
|
+
return;
|
|
267
|
+
const snapshotNodesToProcess = allNodes.filter((node) => isFileNodeKind(node) &&
|
|
268
|
+
node.documentType === "powerhouse/snapshot-report" &&
|
|
269
|
+
!reportingFolderIds.has(node.parentFolder || ""));
|
|
270
|
+
for (const fileNode of snapshotNodesToProcess) {
|
|
271
|
+
if (processedDocs.has(fileNode.id))
|
|
272
|
+
continue;
|
|
273
|
+
const doc = documentsInDrive.find((d) => d.header.documentType === "powerhouse/snapshot-report" &&
|
|
274
|
+
d.header.id === fileNode.id);
|
|
275
|
+
if (!doc)
|
|
276
|
+
continue;
|
|
277
|
+
const reportPeriodStart = doc.state.global.reportPeriodStart;
|
|
278
|
+
const monthName = getMonthNameFromPeriod(reportPeriodStart);
|
|
279
|
+
processedDocs.add(fileNode.id);
|
|
280
|
+
if (monthName) {
|
|
281
|
+
const monthInfo = monthFolders.get(monthName);
|
|
282
|
+
const reportingFolder = monthInfo?.reportingFolder;
|
|
283
|
+
if (reportingFolder) {
|
|
284
|
+
onMoveNode(fileNode, reportingFolder)
|
|
285
|
+
.then(() => {
|
|
286
|
+
cbToast(`Snapshot report "${fileNode.name}" placed in ${monthName} > Reporting`, { type: "success" });
|
|
287
|
+
})
|
|
288
|
+
.catch((error) => {
|
|
289
|
+
console.error(`[DocumentAutoPlacement] Failed to move snapshot report:`, error);
|
|
290
|
+
processedDocs.delete(fileNode.id);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
if (billingFolder && driveId) {
|
|
295
|
+
createMonthFolder(monthName)
|
|
296
|
+
.then(() => {
|
|
297
|
+
processedDocs.delete(fileNode.id);
|
|
298
|
+
})
|
|
299
|
+
.catch(() => {
|
|
300
|
+
processedDocs.delete(fileNode.id);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
processedDocs.delete(fileNode.id);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
cbToast(`Snapshot report "${fileNode.name}" has no report period — could not auto-categorize.`, { type: "warning" });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}, [
|
|
313
|
+
driveId,
|
|
314
|
+
driveDocument,
|
|
315
|
+
documentsInDrive,
|
|
316
|
+
reportingFolderIds,
|
|
317
|
+
monthFolders,
|
|
318
|
+
billingFolder,
|
|
319
|
+
createMonthFolder,
|
|
320
|
+
onMoveNode,
|
|
321
|
+
]);
|
|
322
|
+
// Auto-place billing statements into appropriate Payments folders based on dateIssued
|
|
323
|
+
useEffect(() => {
|
|
324
|
+
if (!driveId || !driveDocument || !documentsInDrive)
|
|
325
|
+
return;
|
|
326
|
+
const allNodes = driveDocument.state.global.nodes;
|
|
327
|
+
const processedDocs = globalProcessingState.processedDocs.get(driveId);
|
|
328
|
+
if (!processedDocs)
|
|
329
|
+
return;
|
|
330
|
+
const billingStatementNodesToProcess = allNodes.filter((node) => isFileNodeKind(node) &&
|
|
331
|
+
node.documentType === "powerhouse/billing-statement" &&
|
|
332
|
+
!paymentsFolderIds.has(node.parentFolder || ""));
|
|
333
|
+
for (const fileNode of billingStatementNodesToProcess) {
|
|
334
|
+
if (processedDocs.has(fileNode.id))
|
|
335
|
+
continue;
|
|
336
|
+
const doc = documentsInDrive.find((d) => d.header.documentType === "powerhouse/billing-statement" &&
|
|
337
|
+
d.header.id === fileNode.id);
|
|
338
|
+
if (!doc)
|
|
339
|
+
continue;
|
|
340
|
+
const dateIssued = doc.state.global.dateIssued;
|
|
341
|
+
const monthName = getMonthNameFromPeriod(dateIssued);
|
|
342
|
+
processedDocs.add(fileNode.id);
|
|
343
|
+
if (monthName) {
|
|
344
|
+
const monthInfo = monthFolders.get(monthName);
|
|
345
|
+
const paymentsFolder = monthInfo?.paymentsFolder;
|
|
346
|
+
if (paymentsFolder) {
|
|
347
|
+
onMoveNode(fileNode, paymentsFolder)
|
|
348
|
+
.then(() => {
|
|
349
|
+
cbToast(`Billing statement "${fileNode.name}" placed in ${monthName} > Payments`, { type: "success" });
|
|
350
|
+
})
|
|
351
|
+
.catch((error) => {
|
|
352
|
+
console.error(`[DocumentAutoPlacement] Failed to move billing statement:`, error);
|
|
353
|
+
processedDocs.delete(fileNode.id);
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
if (billingFolder && driveId) {
|
|
358
|
+
createMonthFolder(monthName)
|
|
359
|
+
.then(() => {
|
|
360
|
+
processedDocs.delete(fileNode.id);
|
|
361
|
+
})
|
|
362
|
+
.catch(() => {
|
|
363
|
+
processedDocs.delete(fileNode.id);
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
processedDocs.delete(fileNode.id);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
cbToast(`Billing statement "${fileNode.name}" has no issue date — could not auto-categorize.`, { type: "warning" });
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}, [
|
|
376
|
+
driveId,
|
|
377
|
+
driveDocument,
|
|
378
|
+
documentsInDrive,
|
|
379
|
+
paymentsFolderIds,
|
|
380
|
+
monthFolders,
|
|
381
|
+
billingFolder,
|
|
382
|
+
createMonthFolder,
|
|
383
|
+
onMoveNode,
|
|
384
|
+
]);
|
|
187
385
|
return {
|
|
188
386
|
isActive: !!driveId,
|
|
189
387
|
};
|