@powerhousedao/codegen 4.1.0-dev.23 → 4.1.0-dev.24

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.
@@ -2,32 +2,43 @@
2
2
  to: "<%= rootDir %>/<%= h.changeCase.param(name) %>/components/CreateDocument.tsx"
3
3
  unless_exists: true
4
4
  ---
5
+ import {
6
+ addDocument,
7
+ useDocumentModelModules,
8
+ useEditorModules,
9
+ useSelectedDriveId,
10
+ useSelectedFolder,
11
+ type VetraDocumentModelModule,
12
+ } from "@powerhousedao/reactor-browser";
5
13
  import { Button } from "@powerhousedao/design-system";
6
- import { type DocumentModelModule } from "document-model";
7
-
8
- interface CreateDocumentProps {
9
- documentModels?: DocumentModelModule[];
10
- createDocument: (doc: DocumentModelModule) => void;
11
- }
12
-
13
- // Helper function to extract document specification from different module formats
14
- function getDocumentSpec(doc: DocumentModelModule) {
15
- if ("documentModelState" in doc) {
16
- return doc.documentModelState as DocumentModelModule["documentModel"];
17
- }
18
-
19
- return doc.documentModel;
20
- }
21
14
 
22
15
  /**
23
16
  * Document creation UI component.
24
17
  * Displays available document types as clickable buttons.
25
- * Customize available document types by filtering documentModels prop.
26
18
  */
27
- export const CreateDocument: React.FC<CreateDocumentProps> = ({
28
- documentModels,
29
- createDocument,
30
- }) => {
19
+ export const CreateDocument = () => {
20
+ const selectedDriveId = useSelectedDriveId();
21
+ const selectedFolder = useSelectedFolder();
22
+ const documentModelModules = useDocumentModelModules();
23
+ const editorModules = useEditorModules();
24
+
25
+ async function handleAddDocument(module: VetraDocumentModelModule) {
26
+ if (!selectedDriveId) {
27
+ return;
28
+ }
29
+ await addDocument(
30
+ selectedDriveId,
31
+ `New ${module.documentModel.id} document`,
32
+ module.documentModel.id,
33
+ selectedFolder?.id,
34
+ undefined,
35
+ undefined,
36
+ editorModules?.find((e) =>
37
+ e.documentTypes.includes(module.documentModel.id),
38
+ )?.id,
39
+ );
40
+ }
41
+
31
42
  return (
32
43
  <div className="px-6">
33
44
  {/* Customize section title here */}
@@ -36,24 +47,25 @@ export const CreateDocument: React.FC<CreateDocumentProps> = ({
36
47
  </h3>
37
48
  {/* Customize layout by changing flex-wrap, gap, or grid layout */}
38
49
  <div className="flex w-full flex-wrap gap-4">
39
- {documentModels?.map((doc) => {
40
- const spec = getDocumentSpec(doc);
50
+ {documentModelModules?.map((documentModelModule) => {
41
51
  return (
42
52
  <Button
43
- key={spec.id}
53
+ key={documentModelModule.documentModel.id}
44
54
  color="light" // Customize button appearance
45
55
  size="small"
46
56
  className="cursor-pointer"
47
- title={spec.name}
48
- aria-description={spec.description}
49
- onClick={() => createDocument(doc)}
57
+ title={documentModelModule.documentModel.name}
58
+ aria-description={documentModelModule.documentModel.description}
59
+ onClick={() => handleAddDocument(documentModelModule)}
50
60
  >
51
61
  {/* Customize document type display format */}
52
- <span className="text-sm">{spec.name}</span>
62
+ <span className="text-sm">
63
+ {documentModelModule.documentModel.name}
64
+ </span>
53
65
  </Button>
54
66
  );
55
67
  })}
56
68
  </div>
57
69
  </div>
58
70
  );
59
- };
71
+ };
@@ -2,60 +2,41 @@
2
2
  to: "<%= rootDir %>/<%= h.changeCase.param(name) %>/components/DriveExplorer.tsx"
3
3
  unless_exists: true
4
4
  ---
5
- import {
6
- FolderItem,
7
- FileItem,
8
- type SharingType,
5
+ import {
9
6
  Breadcrumbs,
7
+ CreateDocumentModal,
8
+ FileItem,
9
+ FolderItem,
10
10
  useBreadcrumbs,
11
- useDrop,
12
11
  } from "@powerhousedao/design-system";
13
- import { useCallback, useState, useRef, useMemo } from "react";
14
- import type { FileNode, FolderNode, Node, GetDocumentOptions } from "document-drive";
15
- import {
16
- useNodes,
17
- useSelectedDrive,
18
- useSelectedFolder,
19
- useFolderChildNodesForId,
20
- useFileChildNodesForId,
21
- useSetSelectedNode,
22
- getDriveSharingType,
23
- makeFolderNodeFromDrive,
24
- } from "@powerhousedao/state";
25
- import { EditorContainer } from "./EditorContainer.js";
26
- import { FolderTree } from "./FolderTree.js";
27
- import type { DocumentModelModule } from "document-model";
28
- import { CreateDocumentModal } from "@powerhousedao/design-system";
29
- import { CreateDocument } from "./CreateDocument.js";
30
12
  import {
31
- type DriveEditorContext,
13
+ addDocument,
14
+ type DriveEditorProps,
15
+ getSyncStatusSync,
16
+ setSelectedNode,
17
+ useAllFolderNodes,
18
+ useDocumentModelModules,
32
19
  useDriveContext,
20
+ useDriveSharingType,
21
+ useEditorModules,
22
+ useFileChildNodes,
23
+ useFolderChildNodes,
24
+ useSelectedDrive,
25
+ useSelectedFolder,
26
+ useSelectedNodePath,
27
+ useUserPermissions,
33
28
  } from "@powerhousedao/reactor-browser";
34
-
35
- interface DriveExplorerProps {
36
- driveId: string;
37
- onAddFolder: (name: string, parent: Node | undefined) => Promise<FolderNode | undefined>;
38
- onRenameNode: (newName: string, node: Node) => Promise<Node | undefined>;
39
- onCopyNode: (src: Node, target: Node | undefined) => Promise<void>;
40
- onOpenDocument: (node: FileNode) => void;
41
- showDeleteNodeModal: (node: Node) => void;
42
- context: DriveEditorContext;
43
- }
29
+ import type { DocumentModelModule } from "document-model";
30
+ import { useCallback, useRef, useState } from "react";
31
+ import { CreateDocument } from "./CreateDocument.jsx";
32
+ import { EditorContainer } from "./EditorContainer.jsx";
33
+ import { FolderTree } from "./FolderTree.jsx";
44
34
 
45
35
  /**
46
36
  * Main drive explorer component with sidebar navigation and content area.
47
37
  * Layout: Left sidebar (folder tree) + Right content area (files/folders + document editor)
48
38
  */
49
- export function DriveExplorer({
50
- driveId,
51
- onRenameNode,
52
- onAddFolder: onAddFolderProp,
53
- onCopyNode,
54
- onOpenDocument,
55
- showDeleteNodeModal,
56
- context,
57
- }: DriveExplorerProps) {
58
-
39
+ export function DriveExplorer(props: DriveEditorProps) {
59
40
  // === DOCUMENT EDITOR STATE ===
60
41
  // Customize document opening/closing behavior here
61
42
  const [activeDocumentId, setActiveDocumentId] = useState<
@@ -63,57 +44,26 @@ export function DriveExplorer({
63
44
  >();
64
45
  const [openModal, setOpenModal] = useState(false);
65
46
  const selectedDocumentModel = useRef<DocumentModelModule | null>(null);
66
-
47
+ const editorModules = useEditorModules();
67
48
  // === DRIVE CONTEXT HOOKS ===
68
49
  // Core drive operations and document models
69
- const {
70
- addDocument,
71
- documentModels, // Modify this to filter available document types
72
- getSyncStatusSync,
73
- isAllowedToCreateDocuments,
50
+ const {
74
51
  onAddFile,
75
52
  onAddFolder,
76
- onAddAndSelectNewFolder,
53
+ onCopyNode,
77
54
  onDuplicateNode,
78
55
  onMoveNode,
56
+ onRenameNode,
57
+ showDeleteNodeModal,
79
58
  } = useDriveContext();
80
-
59
+
60
+ const { isAllowedToCreateDocuments } = useUserPermissions();
81
61
  // === STATE MANAGEMENT HOOKS ===
82
62
  // Core state hooks for drive navigation
83
- const nodes = useNodes(); // All nodes in the drive
84
- const selectedDrive = useSelectedDrive(); // Current drive
63
+ const [selectedDrive] = useSelectedDrive(); // Currently selected drive
85
64
  const selectedFolder = useSelectedFolder(); // Currently selected folder
86
- const setSelectedNode = useSetSelectedNode(); // Function to change selection
87
-
88
- // === BREADCRUMB PATH CALCULATION ===
89
- // Local fix for correct breadcrumb ordering (replaces useSelectedNodePath)
90
- // TODO: Remove this when state package breadcrumb ordering is fixed
91
- const selectedNodePath = useMemo(() => {
92
- if (!nodes || !selectedDrive || !selectedFolder) return [];
93
- const driveFolderNode = makeFolderNodeFromDrive(selectedDrive);
94
-
95
- const path: Node[] = [];
96
- let current = selectedFolder;
97
-
98
- // Build path from current folder up to root
99
- while (current) {
100
- path.unshift(current);
101
- if (!current.parentFolder) break;
102
- current = nodes.find((n) => n.id === current.parentFolder) as FolderNode;
103
- }
104
-
105
- // Add drive at the beginning
106
- if (driveFolderNode) {
107
- path.unshift(driveFolderNode);
108
- }
109
-
110
- return path;
111
- }, [nodes, selectedDrive, selectedFolder]);
112
-
113
- // === COMPUTED VALUES ===
114
- const selectedNodeId = selectedFolder?.id;
115
- const sharingType: SharingType = getDriveSharingType(selectedDrive) as SharingType;
116
- const selectedDriveAsFolderNode = makeFolderNodeFromDrive(selectedDrive);
65
+ const selectedNodePath = useSelectedNodePath();
66
+ const sharingType = useDriveSharingType(selectedDrive?.header.id);
117
67
 
118
68
  // === NAVIGATION SETUP ===
119
69
  // Breadcrumbs for folder navigation
@@ -122,57 +72,35 @@ export function DriveExplorer({
122
72
  setSelectedNode,
123
73
  });
124
74
 
125
- // Drag & drop functionality for file uploads
126
- const { isDropTarget, dropProps } = useDrop({
127
- node: selectedDriveAsFolderNode,
128
- onAddFile,
129
- onCopyNode,
130
- onMoveNode,
131
- });
75
+ const folderChildren = useFolderChildNodes();
76
+ const fileChildren = useFileChildNodes();
132
77
 
133
- // === FOLDER/FILE CHILDREN ===
134
- // Get current folder's contents
135
- const nodeIdForChildren = selectedNodeId === undefined ? null : selectedNodeId;
136
- const folderChildren = useFolderChildNodesForId(nodeIdForChildren);
137
- const fileChildren = useFileChildNodesForId(nodeIdForChildren);
138
-
139
78
  // All folders for the sidebar tree view
140
- const allFolders = nodes?.filter(n => n.kind === "folder") as FolderNode[] || [];
79
+ const allFolders = useAllFolderNodes();
141
80
 
142
81
  // === EVENT HANDLERS ===
143
- // Customize document selection behavior here
144
- const handleFileSelect = useCallback((nodeId: string | undefined) => {
145
- if (!nodeId) return;
146
- const fileNode = nodes?.find(n => n.id === nodeId) as FileNode;
147
- if (fileNode) {
148
- onOpenDocument(fileNode);
149
- }
150
- setActiveDocumentId(nodeId);
151
- }, [nodes, onOpenDocument]);
152
82
 
153
83
  // Handle folder creation with optional name parameter
154
- const handleCreateFolder = useCallback(async (folderName?: string) => {
155
- let name: string | undefined = folderName;
156
-
157
- // If no name provided, prompt for it (for manual folder creation)
158
- if (!name) {
159
- const promptResult = prompt("Enter folder name:");
160
- name = promptResult || undefined;
161
- }
162
-
163
- if (name && name.trim()) {
164
- try {
165
- await onAddFolder(name.trim(), selectedFolder);
166
- } catch (error) {
167
- console.error("Failed to create folder:", error);
84
+ const handleCreateFolder = useCallback(
85
+ async (folderName?: string) => {
86
+ let name: string | undefined = folderName;
87
+
88
+ // If no name provided, prompt for it (for manual folder creation)
89
+ if (!name) {
90
+ const promptResult = prompt("Enter folder name:");
91
+ name = promptResult || undefined;
168
92
  }
169
- }
170
- }, [onAddFolder, selectedFolder]);
171
93
 
172
- // Close document editor and return to folder view
173
- const handleEditorClose = useCallback(() => {
174
- setActiveDocumentId(undefined);
175
- }, []);
94
+ if (name?.trim()) {
95
+ try {
96
+ await onAddFolder(name.trim(), selectedFolder);
97
+ } catch (error) {
98
+ console.error("Failed to create folder:", error);
99
+ }
100
+ }
101
+ },
102
+ [onAddFolder, selectedFolder],
103
+ );
176
104
 
177
105
  // Handle document creation from modal
178
106
  const onCreateDocument = useCallback(
@@ -180,18 +108,22 @@ export function DriveExplorer({
180
108
  setOpenModal(false);
181
109
 
182
110
  const documentModel = selectedDocumentModel.current;
183
- if (!documentModel) return;
111
+ if (!documentModel || !selectedDrive?.header.id) return;
184
112
 
185
113
  try {
186
114
  const node = await addDocument(
187
- driveId,
115
+ selectedDrive.header.id,
188
116
  fileName,
189
117
  documentModel.documentModel.id,
190
- selectedNodeId,
118
+ selectedFolder?.id,
119
+ undefined,
120
+ editorModules?.find((e) =>
121
+ e.documentTypes.includes(documentModel.documentModel.id),
122
+ )?.id,
191
123
  );
192
124
 
193
125
  selectedDocumentModel.current = null;
194
-
126
+
195
127
  if (node) {
196
128
  // Customize: Auto-open created document by uncommenting below
197
129
  // setActiveDocumentId(node.id);
@@ -200,30 +132,12 @@ export function DriveExplorer({
200
132
  console.error("Failed to create document:", error);
201
133
  }
202
134
  },
203
- [addDocument, driveId, selectedNodeId],
204
- );
205
-
206
- // Handle document type selection for creation
207
- const onSelectDocumentModel = useCallback(
208
- (documentModel: DocumentModelModule) => {
209
- selectedDocumentModel.current = documentModel;
210
- setOpenModal(true);
211
- },
212
- [],
213
- );
214
-
215
- // Document revision handling (placeholder implementation)
216
- const onGetDocumentRevision = useCallback(
217
- (_options?: GetDocumentOptions) => {
218
- if (!activeDocumentId) return;
219
- return undefined;
220
- },
221
- [activeDocumentId],
135
+ [addDocument, editorModules, selectedDrive?.header.id, selectedFolder?.id],
222
136
  );
223
137
 
224
138
  // === DOCUMENT EDITOR DATA ===
225
139
  // Filter available document types here if needed
226
- const filteredDocumentModels = documentModels;
140
+ const documentModelModules = useDocumentModelModules();
227
141
 
228
142
  // Get active document and its editor components
229
143
  const activeDocument = activeDocumentId
@@ -231,11 +145,15 @@ export function DriveExplorer({
231
145
  : undefined;
232
146
 
233
147
  const documentModelModule = activeDocument
234
- ? context.getDocumentModelModule(activeDocument.documentType)
148
+ ? documentModelModules?.find(
149
+ (m) => m.documentModel.id === activeDocument.documentType,
150
+ )
235
151
  : null;
236
152
 
237
153
  const editorModule = activeDocument
238
- ? context.getEditor(activeDocument.documentType)
154
+ ? editorModules?.find((e) =>
155
+ e.documentTypes.includes(activeDocument.documentType),
156
+ )
239
157
  : null;
240
158
 
241
159
  // === RENDER ===
@@ -243,41 +161,24 @@ export function DriveExplorer({
243
161
  <div className="flex h-full">
244
162
  {/* === LEFT SIDEBAR: Folder Navigation === */}
245
163
  {/* Customize sidebar width by changing w-64 */}
246
- <div className="w-64 border-r border-gray-200 bg-white overflow-y-auto">
164
+ <div className="w-64 overflow-y-auto border-r border-gray-200 bg-white">
247
165
  <div className="p-4">
248
166
  {/* Customize sidebar title here */}
249
- <h2 className="text-lg font-semibold mb-4 text-gray-700">Drive Explorer</h2>
250
-
167
+ <h2 className="mb-4 text-lg font-semibold text-gray-700">
168
+ Drive Explorer
169
+ </h2>
170
+
251
171
  {/* Folder tree navigation component */}
252
- <FolderTree
253
- folders={allFolders}
254
- selectedNodeId={selectedNodeId}
255
- onSelectNode={setSelectedNode}
256
- />
172
+ <FolderTree folders={allFolders} onSelectNode={setSelectedNode} />
257
173
  </div>
258
174
  </div>
259
175
 
260
176
  {/* === RIGHT CONTENT AREA: Files/Folders or Document Editor === */}
261
- <div
262
- className={`flex-1 p-4 overflow-y-auto ${isDropTarget ? "bg-blue-50 border-2 border-dashed border-blue-300" : ""}`}
263
- {...dropProps}
264
- >
177
+ <div className="flex-1 overflow-y-auto p-4">
265
178
  {/* Conditional rendering: Document editor or folder contents */}
266
179
  {activeDocument && documentModelModule && editorModule ? (
267
180
  // Document editor view
268
- <EditorContainer
269
- context={{
270
- ...context,
271
- getDocumentRevision: onGetDocumentRevision,
272
- }}
273
- documentId={activeDocumentId!}
274
- documentType={activeDocument.documentType}
275
- driveId={driveId}
276
- onClose={handleEditorClose}
277
- title={activeDocument.name}
278
- documentModelModule={documentModelModule}
279
- editorModule={editorModule}
280
- />
181
+ <EditorContainer handleClose={() => setActiveDocumentId(undefined)} />
281
182
  ) : (
282
183
  /* Folder contents view */
283
184
  <div className="space-y-6">
@@ -286,19 +187,21 @@ export function DriveExplorer({
286
187
  <div className="flex items-center justify-between">
287
188
  {/* Folder title */}
288
189
  <h2 className="text-lg font-semibold">
289
- {selectedFolder ? `Contents of "${selectedFolder.name}"` : "Root Contents"}
190
+ {selectedFolder
191
+ ? `Contents of "${selectedFolder.name}"`
192
+ : "Root Contents"}
290
193
  </h2>
291
194
  {/* Customize: Add more action buttons here */}
292
195
  {isAllowedToCreateDocuments && (
293
196
  <button
294
197
  onClick={() => handleCreateFolder()}
295
- className="px-3 py-1 bg-blue-500 text-white rounded text-sm hover:bg-blue-600"
198
+ className="rounded bg-blue-500 px-3 py-1 text-sm text-white hover:bg-blue-600"
296
199
  >
297
200
  + New Folder
298
201
  </button>
299
202
  )}
300
203
  </div>
301
-
204
+
302
205
  {/* Navigation breadcrumbs */}
303
206
  {breadcrumbs.length > 1 && (
304
207
  <div className="border-b border-gray-200 pb-3">
@@ -316,14 +219,16 @@ export function DriveExplorer({
316
219
  {/* Customize grid layout by changing grid-cols-1 */}
317
220
  {folderChildren.length > 0 && (
318
221
  <div>
319
- <h3 className="text-sm font-medium text-gray-500 mb-2">📁 Folders</h3>
222
+ <h3 className="mb-2 text-sm font-medium text-gray-500">
223
+ 📁 Folders
224
+ </h3>
320
225
  <div className="grid grid-cols-1 gap-2">
321
226
  {folderChildren.map((folderNode) => (
322
227
  <FolderItem
323
228
  key={folderNode.id}
324
229
  folderNode={folderNode}
325
230
  isAllowedToCreateDocuments={isAllowedToCreateDocuments}
326
- sharingType={sharingType}
231
+ sharingType={sharingType || "LOCAL"}
327
232
  getSyncStatusSync={getSyncStatusSync}
328
233
  setSelectedNode={setSelectedNode}
329
234
  onAddFile={onAddFile}
@@ -331,8 +236,8 @@ export function DriveExplorer({
331
236
  onMoveNode={onMoveNode}
332
237
  onRenameNode={onRenameNode}
333
238
  onDuplicateNode={onDuplicateNode}
334
- onAddFolder={onAddFolderProp}
335
- onAddAndSelectNewFolder={onAddAndSelectNewFolder}
239
+ onAddFolder={onAddFolder}
240
+ onAddAndSelectNewFolder={handleCreateFolder}
336
241
  showDeleteNodeModal={showDeleteNodeModal}
337
242
  />
338
243
  ))}
@@ -344,24 +249,26 @@ export function DriveExplorer({
344
249
  {/* Customize grid layout by changing grid-cols-1 */}
345
250
  {fileChildren.length > 0 && (
346
251
  <div>
347
- <h3 className="text-sm font-medium text-gray-500 mb-2">📄 Documents</h3>
252
+ <h3 className="mb-2 text-sm font-medium text-gray-500">
253
+ 📄 Documents
254
+ </h3>
348
255
  <div className="grid grid-cols-1 gap-2">
349
256
  {fileChildren.map((fileNode) => (
350
257
  <FileItem
351
258
  key={fileNode.id}
352
259
  fileNode={fileNode}
353
260
  isAllowedToCreateDocuments={isAllowedToCreateDocuments}
354
- sharingType={sharingType}
261
+ sharingType={sharingType || "LOCAL"}
355
262
  getSyncStatusSync={getSyncStatusSync}
356
- setSelectedNode={(nodeId) => handleFileSelect(nodeId)}
263
+ setSelectedNode={setSelectedNode}
357
264
  showDeleteNodeModal={showDeleteNodeModal}
358
265
  onRenameNode={onRenameNode}
359
266
  onDuplicateNode={onDuplicateNode}
360
267
  onAddFile={onAddFile}
361
268
  onCopyNode={onCopyNode}
362
269
  onMoveNode={onMoveNode}
363
- onAddFolder={onAddFolderProp}
364
- onAddAndSelectNewFolder={onAddAndSelectNewFolder}
270
+ onAddFolder={onAddFolder}
271
+ onAddAndSelectNewFolder={handleCreateFolder}
365
272
  />
366
273
  ))}
367
274
  </div>
@@ -371,18 +278,17 @@ export function DriveExplorer({
371
278
  {/* === EMPTY STATE === */}
372
279
  {/* Customize empty state message and styling here */}
373
280
  {folderChildren.length === 0 && fileChildren.length === 0 && (
374
- <div className="text-center py-12 text-gray-500">
281
+ <div className="py-12 text-center text-gray-500">
375
282
  <p className="text-lg">📁 This folder is empty</p>
376
- <p className="text-sm mt-2">Create your first document or folder below</p>
283
+ <p className="mt-2 text-sm">
284
+ Create your first document or folder below
285
+ </p>
377
286
  </div>
378
287
  )}
379
288
 
380
289
  {/* === DOCUMENT CREATION SECTION === */}
381
290
  {/* Component for creating new documents */}
382
- <CreateDocument
383
- createDocument={onSelectDocumentModel}
384
- documentModels={filteredDocumentModels}
385
- />
291
+ <CreateDocument />
386
292
  </div>
387
293
  )}
388
294
  </div>
@@ -396,4 +302,4 @@ export function DriveExplorer({
396
302
  />
397
303
  </div>
398
304
  );
399
- }
305
+ }