@powerhousedao/network-admin 0.0.55 → 0.0.57
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/editors/builders/editor.d.ts.map +1 -1
- package/dist/editors/builders/editor.js +150 -69
- package/dist/editors/builders/hooks/useRemoteBuilderProfiles.d.ts +20 -0
- package/dist/editors/builders/hooks/useRemoteBuilderProfiles.d.ts.map +1 -0
- package/dist/editors/builders/hooks/useRemoteBuilderProfiles.js +57 -0
- package/dist/editors/builders/utils/graphql-client.d.ts +41 -0
- package/dist/editors/builders/utils/graphql-client.d.ts.map +1 -0
- package/dist/editors/builders/utils/graphql-client.js +189 -0
- package/dist/editors/network-admin/components/DriveExplorer.d.ts.map +1 -1
- package/dist/editors/network-admin/components/DriveExplorer.js +0 -2
- package/dist/scripts/sow-mirror/mirror_sow_state.js +1 -0
- package/dist/style.css +26 -3
- package/dist/subgraphs/builders-addon/resolvers.d.ts.map +1 -1
- package/dist/subgraphs/builders-addon/resolvers.js +117 -25
- package/dist/subgraphs/builders-addon/schema.d.ts.map +1 -1
- package/dist/subgraphs/builders-addon/schema.js +4 -6
- package/dist/subgraphs/networks/resolvers.d.ts.map +1 -1
- package/dist/subgraphs/networks/resolvers.js +4 -2
- package/dist/subgraphs/networks/schema.js +1 -1
- package/dist/subgraphs/workstreams/resolvers.d.ts.map +1 -1
- package/dist/subgraphs/workstreams/resolvers.js +7 -5
- package/dist/subgraphs/workstreams/schema.d.ts.map +1 -1
- package/dist/subgraphs/workstreams/schema.js +1 -3
- package/package.json +2 -2
- package/dist/editors/network-admin/components/builders.d.ts +0 -2
- package/dist/editors/network-admin/components/builders.d.ts.map +0 -1
- package/dist/editors/network-admin/components/builders.js +0 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../editors/builders/editor.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../editors/builders/editor.tsx"],"names":[],"mappings":"AAgJA,0CAA0C;AAC1C,MAAM,CAAC,OAAO,UAAU,MAAM,4CAkT7B"}
|
|
@@ -2,9 +2,71 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { DocumentToolbar } from "@powerhousedao/design-system/connect";
|
|
3
3
|
import { useSelectedBuildersDocument } from "../../document-models/builders/hooks.js";
|
|
4
4
|
import { actions as buildersActions } from "../../document-models/builders/actions.js";
|
|
5
|
-
import { setSelectedNode, useParentFolderForSelectedNode,
|
|
6
|
-
import { useMemo, useCallback } from "react";
|
|
5
|
+
import { setSelectedNode, useParentFolderForSelectedNode, useDrives, useGetDocuments, } from "@powerhousedao/reactor-browser";
|
|
6
|
+
import { useMemo, useCallback, useState, useEffect } from "react";
|
|
7
7
|
import { ObjectSetTable, PHIDInput, } from "@powerhousedao/document-engineering";
|
|
8
|
+
import { useRemoteBuilderProfiles } from "./hooks/useRemoteBuilderProfiles.js";
|
|
9
|
+
/**
|
|
10
|
+
* Wrapper component for PHIDInput that properly tracks selected PHID
|
|
11
|
+
* and handles saving on blur/enter with the correct PHID value.
|
|
12
|
+
*/
|
|
13
|
+
function BuilderPHIDInput({ initialPhid, options, onSave, fetchOptionsCallback, }) {
|
|
14
|
+
const [inputText, setInputText] = useState("");
|
|
15
|
+
const [hasSaved, setHasSaved] = useState(false);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
setInputText("");
|
|
18
|
+
setHasSaved(false);
|
|
19
|
+
}, [initialPhid]);
|
|
20
|
+
const findPhidByInput = useCallback((input) => {
|
|
21
|
+
const trimmed = input.trim();
|
|
22
|
+
if (!trimmed)
|
|
23
|
+
return null;
|
|
24
|
+
const lowerInput = trimmed.toLowerCase();
|
|
25
|
+
const exactMatchByName = options.find((opt) => opt.label.toLowerCase() === lowerInput);
|
|
26
|
+
if (exactMatchByName)
|
|
27
|
+
return exactMatchByName.id;
|
|
28
|
+
const partialMatchByName = options.find((opt) => opt.label.toLowerCase().startsWith(lowerInput));
|
|
29
|
+
if (partialMatchByName)
|
|
30
|
+
return partialMatchByName.id;
|
|
31
|
+
const containsMatches = options.filter((opt) => opt.label.toLowerCase().includes(lowerInput));
|
|
32
|
+
if (containsMatches.length === 1)
|
|
33
|
+
return containsMatches[0].id;
|
|
34
|
+
const matchById = options.find((opt) => opt.id.toLowerCase() === lowerInput);
|
|
35
|
+
if (matchById)
|
|
36
|
+
return matchById.id;
|
|
37
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
38
|
+
if (uuidRegex.test(trimmed))
|
|
39
|
+
return trimmed;
|
|
40
|
+
return null;
|
|
41
|
+
}, [options]);
|
|
42
|
+
const isKnownPhid = useCallback((value) => {
|
|
43
|
+
return options.some((opt) => opt.id === value);
|
|
44
|
+
}, [options]);
|
|
45
|
+
const savePhid = useCallback((phid) => {
|
|
46
|
+
if (!hasSaved && phid && phid !== initialPhid) {
|
|
47
|
+
setHasSaved(true);
|
|
48
|
+
onSave(phid);
|
|
49
|
+
}
|
|
50
|
+
}, [hasSaved, initialPhid, onSave]);
|
|
51
|
+
const handleBlur = useCallback(() => {
|
|
52
|
+
if (hasSaved)
|
|
53
|
+
return;
|
|
54
|
+
if (inputText) {
|
|
55
|
+
const foundPhid = findPhidByInput(inputText);
|
|
56
|
+
if (foundPhid) {
|
|
57
|
+
savePhid(foundPhid);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}, [hasSaved, inputText, findPhidByInput, savePhid]);
|
|
61
|
+
return (_jsx(PHIDInput, { value: initialPhid, onChange: (newValue) => {
|
|
62
|
+
if (isKnownPhid(newValue)) {
|
|
63
|
+
savePhid(newValue);
|
|
64
|
+
}
|
|
65
|
+
}, onInput: (e) => {
|
|
66
|
+
const target = e.target;
|
|
67
|
+
setInputText(target.value);
|
|
68
|
+
}, onBlur: handleBlur, placeholder: "Enter PHID or search by name", className: "w-full", variant: "withValueAndTitle", initialOptions: options, fetchOptionsCallback: fetchOptionsCallback }));
|
|
69
|
+
}
|
|
8
70
|
/** Implement your editor behavior here */
|
|
9
71
|
export default function Editor() {
|
|
10
72
|
const [doc, dispatch] = useSelectedBuildersDocument();
|
|
@@ -35,11 +97,11 @@ export default function Editor() {
|
|
|
35
97
|
}, [builderProfileNodesWithDriveId]);
|
|
36
98
|
// Fetch all builder profile documents from all drives
|
|
37
99
|
const builderProfileDocuments = useGetDocuments(builderPhids);
|
|
38
|
-
// Create a map of PHID to document for quick lookup
|
|
39
|
-
const
|
|
40
|
-
if (!builderProfileDocuments)
|
|
41
|
-
return new Map();
|
|
100
|
+
// Create a map of PHID to document for quick lookup (local drives)
|
|
101
|
+
const localBuilderProfileMap = useMemo(() => {
|
|
42
102
|
const map = new Map();
|
|
103
|
+
if (!builderProfileDocuments)
|
|
104
|
+
return map;
|
|
43
105
|
builderProfileDocuments.forEach((doc) => {
|
|
44
106
|
if (doc.header.documentType === "powerhouse/builder-profile") {
|
|
45
107
|
map.set(doc.header.id, doc);
|
|
@@ -47,10 +109,13 @@ export default function Editor() {
|
|
|
47
109
|
});
|
|
48
110
|
return map;
|
|
49
111
|
}, [builderProfileDocuments]);
|
|
50
|
-
//
|
|
112
|
+
// Fetch remote profiles as fallback for builders not found locally
|
|
113
|
+
const { profileMap: remoteProfileMap, allProfiles: remoteProfiles } = useRemoteBuilderProfiles(localBuilderProfileMap);
|
|
114
|
+
// Helper function to get builder profile documents from all drives (local + remote)
|
|
51
115
|
const getBuilderProfiles = useCallback(() => {
|
|
52
|
-
|
|
53
|
-
|
|
116
|
+
// Start with local profiles
|
|
117
|
+
const profileOptions = builderProfileNodesWithDriveId.map(({ node }) => {
|
|
118
|
+
const doc = localBuilderProfileMap.get(node.id);
|
|
54
119
|
const name = doc?.state?.global?.name || node.name || node.id;
|
|
55
120
|
return {
|
|
56
121
|
id: node.id,
|
|
@@ -59,26 +124,54 @@ export default function Editor() {
|
|
|
59
124
|
title: name,
|
|
60
125
|
};
|
|
61
126
|
});
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
127
|
+
// Add remote profiles that aren't already in local
|
|
128
|
+
const localIds = new Set(profileOptions.map((p) => p.id));
|
|
129
|
+
for (const remoteProfile of remoteProfiles) {
|
|
130
|
+
if (!localIds.has(remoteProfile.id)) {
|
|
131
|
+
const name = remoteProfile.state?.name || remoteProfile.id;
|
|
132
|
+
profileOptions.push({
|
|
133
|
+
id: remoteProfile.id,
|
|
134
|
+
label: name,
|
|
135
|
+
value: remoteProfile.id,
|
|
136
|
+
title: name,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return profileOptions;
|
|
141
|
+
}, [builderProfileNodesWithDriveId, localBuilderProfileMap, remoteProfiles]);
|
|
142
|
+
// Helper function to get builder profile data by PHID (local first, then remote fallback)
|
|
143
|
+
const getBuilderProfileByPhid = useCallback((phid) => {
|
|
144
|
+
// Try local first
|
|
145
|
+
const localDoc = localBuilderProfileMap.get(phid);
|
|
146
|
+
if (localDoc) {
|
|
147
|
+
return {
|
|
148
|
+
name: localDoc.state.global?.name || localDoc.header.id,
|
|
149
|
+
slug: localDoc.state.global?.slug || localDoc.header.id,
|
|
150
|
+
icon: localDoc.state.global?.icon || null,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
// Fall back to remote
|
|
154
|
+
const remoteProfile = remoteProfileMap.get(phid);
|
|
155
|
+
if (remoteProfile) {
|
|
156
|
+
return {
|
|
157
|
+
name: remoteProfile.state?.name || remoteProfile.id,
|
|
158
|
+
slug: remoteProfile.state?.slug || remoteProfile.id,
|
|
159
|
+
icon: remoteProfile.state?.icon || null,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}, [localBuilderProfileMap, remoteProfileMap]);
|
|
74
164
|
const builders = useMemo(() => {
|
|
75
|
-
return doc?.state.global.builders.map((phid) =>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
165
|
+
return (doc?.state.global.builders.map((phid) => {
|
|
166
|
+
const profile = getBuilderProfileByPhid(phid);
|
|
167
|
+
return {
|
|
168
|
+
phid: phid,
|
|
169
|
+
name: profile?.name || "",
|
|
170
|
+
slug: profile?.slug || "",
|
|
171
|
+
icon: profile?.icon || null,
|
|
172
|
+
};
|
|
173
|
+
}) || []);
|
|
174
|
+
}, [doc, getBuilderProfileByPhid]);
|
|
82
175
|
const columns = useMemo(() => [
|
|
83
176
|
{
|
|
84
177
|
field: "phid",
|
|
@@ -89,9 +182,9 @@ export default function Editor() {
|
|
|
89
182
|
onSave: (newValue, context) => {
|
|
90
183
|
const currentId = context.row.phid || "";
|
|
91
184
|
if (newValue !== currentId && newValue && currentId) {
|
|
92
|
-
// First remove the old
|
|
185
|
+
// First remove the old builder
|
|
93
186
|
dispatch(buildersActions.removeBuilder({ builderPhid: currentId }));
|
|
94
|
-
// Then add the new
|
|
187
|
+
// Then add the new builder with the new PHID
|
|
95
188
|
dispatch(buildersActions.addBuilder({
|
|
96
189
|
builderPhid: newValue,
|
|
97
190
|
}));
|
|
@@ -99,48 +192,38 @@ export default function Editor() {
|
|
|
99
192
|
}
|
|
100
193
|
return false;
|
|
101
194
|
},
|
|
102
|
-
renderCellEditor: (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const newValue = e.target.value;
|
|
106
|
-
const currentValue = value || "";
|
|
195
|
+
renderCellEditor: (_value, _onChange, context) => {
|
|
196
|
+
const currentPhid = context.row.phid || "";
|
|
197
|
+
const handleSave = (phidValue) => {
|
|
107
198
|
// If a PHID is entered and it's different from current value
|
|
108
|
-
if (
|
|
109
|
-
const
|
|
110
|
-
const existingBuilder = builders?.find((builder) => builder?.phid === newValue);
|
|
199
|
+
if (phidValue && phidValue !== currentPhid) {
|
|
200
|
+
const existingBuilder = builders.find((builder) => builder.phid === phidValue);
|
|
111
201
|
if (!existingBuilder) {
|
|
112
202
|
// If we're editing an existing row (has an ID), remove the old one first
|
|
113
|
-
if (
|
|
203
|
+
if (currentPhid && currentPhid !== phidValue) {
|
|
114
204
|
dispatch(buildersActions.removeBuilder({
|
|
115
|
-
builderPhid:
|
|
116
|
-
}));
|
|
117
|
-
}
|
|
118
|
-
if (builderProfile) {
|
|
119
|
-
// Create new agent with data from builder profile
|
|
120
|
-
dispatch(buildersActions.addBuilder({
|
|
121
|
-
builderPhid: newValue,
|
|
122
|
-
}));
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
// Manual PHID entry - create agent with empty data that user can fill
|
|
126
|
-
dispatch(buildersActions.addBuilder({
|
|
127
|
-
builderPhid: newValue,
|
|
205
|
+
builderPhid: currentPhid,
|
|
128
206
|
}));
|
|
129
207
|
}
|
|
208
|
+
// Add the new builder
|
|
209
|
+
dispatch(buildersActions.addBuilder({
|
|
210
|
+
builderPhid: phidValue,
|
|
211
|
+
}));
|
|
130
212
|
}
|
|
131
213
|
}
|
|
132
|
-
}
|
|
214
|
+
};
|
|
215
|
+
const fetchOptions = (userInput) => {
|
|
133
216
|
const builderProfiles = getBuilderProfiles();
|
|
134
217
|
// Filter profiles based on user input
|
|
135
218
|
if (!userInput.trim()) {
|
|
136
|
-
return builderProfiles;
|
|
219
|
+
return Promise.resolve(builderProfiles);
|
|
137
220
|
}
|
|
138
|
-
const filteredProfiles = builderProfiles.filter((profile) => profile.label
|
|
139
|
-
.toLowerCase()
|
|
140
|
-
.includes(userInput.toLowerCase()) ||
|
|
221
|
+
const filteredProfiles = builderProfiles.filter((profile) => profile.label.toLowerCase().includes(userInput.toLowerCase()) ||
|
|
141
222
|
profile.id.toLowerCase().includes(userInput.toLowerCase()));
|
|
142
|
-
return filteredProfiles;
|
|
143
|
-
}
|
|
223
|
+
return Promise.resolve(filteredProfiles);
|
|
224
|
+
};
|
|
225
|
+
return (_jsx(BuilderPHIDInput, { initialPhid: currentPhid, options: getBuilderProfiles(), onSave: handleSave, fetchOptionsCallback: fetchOptions }, `phid-input-${currentPhid || Date.now()}`));
|
|
226
|
+
},
|
|
144
227
|
renderCell: (value) => {
|
|
145
228
|
if (value === "" || !value) {
|
|
146
229
|
return (_jsx("div", { className: "font-light italic text-gray-500 text-center", children: "+ Double-click to add new builder (enter or click outside to save)" }));
|
|
@@ -174,30 +257,28 @@ export default function Editor() {
|
|
|
174
257
|
editable: false,
|
|
175
258
|
align: "center",
|
|
176
259
|
width: 150,
|
|
177
|
-
renderCell: (
|
|
260
|
+
renderCell: (_value, context) => {
|
|
178
261
|
if (!context.row.icon) {
|
|
179
262
|
return null;
|
|
180
263
|
}
|
|
181
|
-
return (_jsx("div", { className: "text-center", children: _jsx("img", { src: context.row.icon, alt: "
|
|
264
|
+
return (_jsx("div", { className: "text-center", children: _jsx("img", { src: context.row.icon, alt: "Builder icon", className: "w-10 h-10 rounded-sm mx-auto object-cover", onError: (e) => {
|
|
182
265
|
e.currentTarget.style.display = "none";
|
|
183
|
-
e.currentTarget.nextElementSibling?.classList.remove("hidden");
|
|
184
266
|
} }) }));
|
|
185
267
|
},
|
|
186
268
|
},
|
|
187
|
-
], [builders,
|
|
188
|
-
return (_jsxs("div", { className: "w-full bg-gray-50", children: [_jsx(DocumentToolbar, { document: doc, onClose: handleClose }), _jsxs("div", { className: "p-2 max-w-4xl mx-auto min-h-screen", children: [_jsx("div", { className: "bg-white rounded-lg p-6 mb-6 shadow-sm text-center", children: _jsx("h1", { className: "text-3xl font-bold text-gray-900 mb-2", children: "Builders" }) }), _jsx("div", { className: "mt-4 bg-white", children: _jsx(ObjectSetTable, { columns: columns, data: builders
|
|
269
|
+
], [builders, getBuilderProfiles, dispatch]);
|
|
270
|
+
return (_jsxs("div", { className: "w-full bg-gray-50", children: [_jsx(DocumentToolbar, { document: doc, onClose: handleClose }), _jsxs("div", { className: "p-2 max-w-4xl mx-auto min-h-screen", children: [_jsx("div", { className: "bg-white rounded-lg p-6 mb-6 shadow-sm text-center", children: _jsx("h1", { className: "text-3xl font-bold text-gray-900 mb-2", children: "Builders" }) }), _jsx("div", { className: "mt-4 bg-white", children: _jsx(ObjectSetTable, { columns: columns, data: builders, allowRowSelection: true, onDelete: (data) => {
|
|
189
271
|
if (data.length > 0) {
|
|
190
272
|
data.forEach((d) => {
|
|
191
273
|
dispatch(buildersActions.removeBuilder({ builderPhid: d.phid }));
|
|
192
274
|
});
|
|
193
275
|
}
|
|
194
276
|
}, onAdd: (data) => {
|
|
195
|
-
// Only add if we have a
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const tempId = data.id || `temp-${Date.now()}`;
|
|
277
|
+
// Only add if we have a PHID
|
|
278
|
+
const phid = data.id;
|
|
279
|
+
if (phid) {
|
|
199
280
|
dispatch(buildersActions.addBuilder({
|
|
200
|
-
builderPhid:
|
|
281
|
+
builderPhid: phid,
|
|
201
282
|
}));
|
|
202
283
|
}
|
|
203
284
|
} }) })] })] }));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type RemoteBuilderProfile } from "../utils/graphql-client.js";
|
|
2
|
+
interface UseRemoteBuilderProfilesResult {
|
|
3
|
+
/** Map of PHID to remote builder profile data */
|
|
4
|
+
profileMap: Map<string, RemoteBuilderProfile>;
|
|
5
|
+
/** All available remote profiles for selection */
|
|
6
|
+
allProfiles: RemoteBuilderProfile[];
|
|
7
|
+
/** Whether remote data is currently loading */
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
/** Manually refetch all available profiles */
|
|
10
|
+
refetchAll: () => Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Hook for fetching builder profiles from remote Switchboard drives.
|
|
14
|
+
* Used as a fallback when local drives don't have the builder profile documents.
|
|
15
|
+
*
|
|
16
|
+
* @param localProfileMap - Map of PHIDs that are already resolved locally (to avoid using remote data for those)
|
|
17
|
+
*/
|
|
18
|
+
export declare function useRemoteBuilderProfiles(localProfileMap: Map<string, unknown>): UseRemoteBuilderProfilesResult;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=useRemoteBuilderProfiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRemoteBuilderProfiles.d.ts","sourceRoot":"","sources":["../../../../editors/builders/hooks/useRemoteBuilderProfiles.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,4BAA4B,CAAC;AAEpC,UAAU,8BAA8B;IACtC,iDAAiD;IACjD,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC9C,kDAAkD;IAClD,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,+CAA+C;IAC/C,SAAS,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GACpC,8BAA8B,CA8DhC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useRef } from "react";
|
|
2
|
+
import { fetchAllRemoteBuilderProfiles, } from "../utils/graphql-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Hook for fetching builder profiles from remote Switchboard drives.
|
|
5
|
+
* Used as a fallback when local drives don't have the builder profile documents.
|
|
6
|
+
*
|
|
7
|
+
* @param localProfileMap - Map of PHIDs that are already resolved locally (to avoid using remote data for those)
|
|
8
|
+
*/
|
|
9
|
+
export function useRemoteBuilderProfiles(localProfileMap) {
|
|
10
|
+
const [profileMap, setProfileMap] = useState(new Map());
|
|
11
|
+
const [allProfiles, setAllProfiles] = useState([]);
|
|
12
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
13
|
+
// Track if we've already started fetching to avoid duplicate requests
|
|
14
|
+
const isFetchingRef = useRef(false);
|
|
15
|
+
const hasFetchedRef = useRef(false);
|
|
16
|
+
// Fetch all available profiles
|
|
17
|
+
const refetchAll = useCallback(async () => {
|
|
18
|
+
// Prevent concurrent fetches
|
|
19
|
+
if (isFetchingRef.current) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
isFetchingRef.current = true;
|
|
23
|
+
setIsLoading(true);
|
|
24
|
+
try {
|
|
25
|
+
const profiles = await fetchAllRemoteBuilderProfiles();
|
|
26
|
+
hasFetchedRef.current = true;
|
|
27
|
+
setAllProfiles(profiles);
|
|
28
|
+
// Build profile map
|
|
29
|
+
const newMap = new Map();
|
|
30
|
+
profiles.forEach((profile) => {
|
|
31
|
+
newMap.set(profile.id, profile);
|
|
32
|
+
});
|
|
33
|
+
setProfileMap(newMap);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.warn("[useRemoteBuilderProfiles] Failed to fetch profiles:", error);
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
setIsLoading(false);
|
|
40
|
+
isFetchingRef.current = false;
|
|
41
|
+
}
|
|
42
|
+
}, []);
|
|
43
|
+
// Auto-fetch all profiles on mount
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!hasFetchedRef.current && !isFetchingRef.current) {
|
|
46
|
+
void refetchAll();
|
|
47
|
+
}
|
|
48
|
+
}, [refetchAll]);
|
|
49
|
+
// Filter out profiles that exist locally from the returned allProfiles
|
|
50
|
+
const filteredAllProfiles = allProfiles.filter((profile) => !localProfileMap.has(profile.id));
|
|
51
|
+
return {
|
|
52
|
+
profileMap,
|
|
53
|
+
allProfiles: filteredAllProfiles,
|
|
54
|
+
isLoading,
|
|
55
|
+
refetchAll,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL client utility for fetching remote builder profiles from Switchboard.
|
|
3
|
+
* This is used as a fallback when local drives don't have the builder profile documents.
|
|
4
|
+
*/
|
|
5
|
+
export interface RemoteBuilderProfile {
|
|
6
|
+
id: string;
|
|
7
|
+
state: {
|
|
8
|
+
name: string | null;
|
|
9
|
+
slug: string | null;
|
|
10
|
+
icon: string | null;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Fetches all available remote drives
|
|
15
|
+
*/
|
|
16
|
+
export declare function fetchRemoteDrives(): Promise<string[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Fetches drive ID by slug
|
|
19
|
+
*/
|
|
20
|
+
export declare function fetchDriveIdBySlug(slug: string): Promise<string | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Fetches all builder profiles from a specific drive
|
|
23
|
+
*/
|
|
24
|
+
export declare function fetchBuilderProfilesFromDrive(driveId: string, options?: {
|
|
25
|
+
silent?: boolean;
|
|
26
|
+
}): Promise<RemoteBuilderProfile[]>;
|
|
27
|
+
/**
|
|
28
|
+
* Fetches a single builder profile by document ID
|
|
29
|
+
*/
|
|
30
|
+
export declare function fetchBuilderProfileById(docId: string, driveId?: string): Promise<RemoteBuilderProfile | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Fetches all builder profiles from all available remote drives.
|
|
33
|
+
* This aggregates profiles from multiple drives into a single list.
|
|
34
|
+
*/
|
|
35
|
+
export declare function fetchAllRemoteBuilderProfiles(): Promise<RemoteBuilderProfile[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Fetches multiple builder profiles by their IDs.
|
|
38
|
+
* Tries to find them across all available remote drives.
|
|
39
|
+
*/
|
|
40
|
+
export declare function fetchRemoteBuilderProfilesByIds(phids: string[]): Promise<Map<string, RemoteBuilderProfile>>;
|
|
41
|
+
//# sourceMappingURL=graphql-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graphql-client.d.ts","sourceRoot":"","sources":["../../../../editors/builders/utils/graphql-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAmIH,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;KACrB,CAAC;CACH;AAcD;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAG3D;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAM7E;AAED;;GAEG;AACH,wBAAsB,6BAA6B,CACjD,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAOjC;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAMtC;AAED;;;GAGG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAC5D,oBAAoB,EAAE,CACvB,CA8BA;AAED;;;GAGG;AACH,wBAAsB,+BAA+B,CACnD,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAiC5C"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL client utility for fetching remote builder profiles from Switchboard.
|
|
3
|
+
* This is used as a fallback when local drives don't have the builder profile documents.
|
|
4
|
+
*/
|
|
5
|
+
function getGraphQLUrl() {
|
|
6
|
+
if (typeof window === "undefined") {
|
|
7
|
+
return "http://localhost:4001/graphql";
|
|
8
|
+
}
|
|
9
|
+
const baseURI = window.document.baseURI;
|
|
10
|
+
if (baseURI.includes("localhost")) {
|
|
11
|
+
return "http://localhost:4001/graphql";
|
|
12
|
+
}
|
|
13
|
+
// Determine the appropriate Switchboard URL based on environment
|
|
14
|
+
if (baseURI.includes("-dev.")) {
|
|
15
|
+
return "https://switchboard-dev.powerhouse.xyz/graphql";
|
|
16
|
+
}
|
|
17
|
+
if (baseURI.includes("-staging.")) {
|
|
18
|
+
return "https://switchboard-staging.powerhouse.xyz/graphql";
|
|
19
|
+
}
|
|
20
|
+
// Production environment
|
|
21
|
+
return "https://switchboard.powerhouse.xyz/graphql";
|
|
22
|
+
}
|
|
23
|
+
async function graphqlRequest(query, variables, options) {
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(getGraphQLUrl(), {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: {
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
},
|
|
30
|
+
body: JSON.stringify({ query, variables }),
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
if (!options?.silent) {
|
|
34
|
+
console.warn("[graphql-client] Request failed:", response.status, response.statusText);
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const result = (await response.json());
|
|
39
|
+
// Return data even if there are errors - partial data might still be useful
|
|
40
|
+
// Only treat as full failure if there's no data at all
|
|
41
|
+
if (result.errors?.length && !result.data) {
|
|
42
|
+
if (!options?.silent) {
|
|
43
|
+
console.warn("[graphql-client] GraphQL errors:", result.errors);
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return result.data ?? null;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
// Silently fail - this is a fallback mechanism
|
|
51
|
+
if (!options?.silent) {
|
|
52
|
+
console.warn("[graphql-client] Request error:", error);
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Query to get all available drives
|
|
58
|
+
const GET_DRIVES_QUERY = `
|
|
59
|
+
query GetDrives {
|
|
60
|
+
drives
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
// Query to get drive ID by slug
|
|
64
|
+
const GET_DRIVE_ID_BY_SLUG_QUERY = `
|
|
65
|
+
query GetDriveIdBySlug($slug: String!) {
|
|
66
|
+
driveIdBySlug(slug: $slug)
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
// Query to get builder profile documents from a drive
|
|
70
|
+
const GET_BUILDER_PROFILES_QUERY = `
|
|
71
|
+
query GetBuilderProfiles($driveId: String!) {
|
|
72
|
+
BuilderProfile {
|
|
73
|
+
getDocuments(driveId: $driveId) {
|
|
74
|
+
id
|
|
75
|
+
state {
|
|
76
|
+
name
|
|
77
|
+
slug
|
|
78
|
+
icon
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
`;
|
|
84
|
+
// Query to get a single builder profile by ID
|
|
85
|
+
const GET_BUILDER_PROFILE_QUERY = `
|
|
86
|
+
query GetBuilderProfile($docId: PHID!, $driveId: PHID) {
|
|
87
|
+
BuilderProfile {
|
|
88
|
+
getDocument(docId: $docId, driveId: $driveId) {
|
|
89
|
+
id
|
|
90
|
+
state {
|
|
91
|
+
name
|
|
92
|
+
slug
|
|
93
|
+
icon
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
`;
|
|
99
|
+
/**
|
|
100
|
+
* Fetches all available remote drives
|
|
101
|
+
*/
|
|
102
|
+
export async function fetchRemoteDrives() {
|
|
103
|
+
const data = await graphqlRequest(GET_DRIVES_QUERY);
|
|
104
|
+
return data?.drives ?? [];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Fetches drive ID by slug
|
|
108
|
+
*/
|
|
109
|
+
export async function fetchDriveIdBySlug(slug) {
|
|
110
|
+
const data = await graphqlRequest(GET_DRIVE_ID_BY_SLUG_QUERY, { slug });
|
|
111
|
+
return data?.driveIdBySlug ?? null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Fetches all builder profiles from a specific drive
|
|
115
|
+
*/
|
|
116
|
+
export async function fetchBuilderProfilesFromDrive(driveId, options) {
|
|
117
|
+
const data = await graphqlRequest(GET_BUILDER_PROFILES_QUERY, { driveId }, options);
|
|
118
|
+
return data?.BuilderProfile?.getDocuments ?? [];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Fetches a single builder profile by document ID
|
|
122
|
+
*/
|
|
123
|
+
export async function fetchBuilderProfileById(docId, driveId) {
|
|
124
|
+
const data = await graphqlRequest(GET_BUILDER_PROFILE_QUERY, { docId, driveId });
|
|
125
|
+
return data?.BuilderProfile?.getDocument ?? null;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Fetches all builder profiles from all available remote drives.
|
|
129
|
+
* This aggregates profiles from multiple drives into a single list.
|
|
130
|
+
*/
|
|
131
|
+
export async function fetchAllRemoteBuilderProfiles() {
|
|
132
|
+
try {
|
|
133
|
+
const drives = await fetchRemoteDrives();
|
|
134
|
+
if (!drives.length) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
// Fetch profiles from all drives in parallel (silent to avoid console spam)
|
|
138
|
+
const profilePromises = drives.map((driveSlug) => fetchBuilderProfilesFromDrive(driveSlug, { silent: true }).catch(() => []));
|
|
139
|
+
const profileArrays = await Promise.all(profilePromises);
|
|
140
|
+
// Flatten and dedupe by ID
|
|
141
|
+
const profileMap = new Map();
|
|
142
|
+
for (const profiles of profileArrays) {
|
|
143
|
+
for (const profile of profiles) {
|
|
144
|
+
if (!profileMap.has(profile.id)) {
|
|
145
|
+
profileMap.set(profile.id, profile);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return Array.from(profileMap.values());
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Fetches multiple builder profiles by their IDs.
|
|
157
|
+
* Tries to find them across all available remote drives.
|
|
158
|
+
*/
|
|
159
|
+
export async function fetchRemoteBuilderProfilesByIds(phids) {
|
|
160
|
+
if (!phids.length) {
|
|
161
|
+
return new Map();
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
// First, get all profiles from all drives
|
|
165
|
+
const allProfiles = await fetchAllRemoteBuilderProfiles();
|
|
166
|
+
// Filter to only the ones we need
|
|
167
|
+
const result = new Map();
|
|
168
|
+
for (const profile of allProfiles) {
|
|
169
|
+
if (phids.includes(profile.id)) {
|
|
170
|
+
result.set(profile.id, profile);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// For any missing profiles, try direct fetch
|
|
174
|
+
const missingPhids = phids.filter((phid) => !result.has(phid));
|
|
175
|
+
if (missingPhids.length > 0) {
|
|
176
|
+
const directFetches = missingPhids.map(async (phid) => {
|
|
177
|
+
const profile = await fetchBuilderProfileById(phid);
|
|
178
|
+
if (profile) {
|
|
179
|
+
result.set(phid, profile);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
await Promise.all(directFetches);
|
|
183
|
+
}
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return new Map();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DriveExplorer.d.ts","sourceRoot":"","sources":["../../../../editors/network-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/network-admin/components/DriveExplorer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAmDlD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAkpBtD"}
|
|
@@ -3,7 +3,6 @@ import { useState, useEffect, useMemo, useCallback, useRef } from "react";
|
|
|
3
3
|
import { Sidebar, SidebarProvider, Button, } from "@powerhousedao/document-engineering";
|
|
4
4
|
import { setSelectedNode, useSelectedDrive, useSelectedFolder, useUserPermissions, useDocumentsInSelectedDrive, useFileNodesInSelectedDrive, useNodeActions, useSelectedDocument, showDeleteNodeModal, addDocument, dispatchActions, } from "@powerhousedao/reactor-browser";
|
|
5
5
|
import { CreateDocumentModal } from "@powerhousedao/design-system/connect";
|
|
6
|
-
import {} from "document-drive";
|
|
7
6
|
import {} from "document-model";
|
|
8
7
|
import { PaymentIcon } from "./icons/PaymentIcon.js";
|
|
9
8
|
import { RfpIcon } from "./icons/RfpIcon.js";
|
|
@@ -11,7 +10,6 @@ import { SowIcon } from "./icons/SowIcon.js";
|
|
|
11
10
|
import { WorkstreamIcon } from "./icons/WorkstreamIcon.js";
|
|
12
11
|
import { Earth } from "lucide-react";
|
|
13
12
|
import { editClientInfo, editWorkstream, } from "../../../document-models/workstream/gen/creators.js";
|
|
14
|
-
import Builders from "./builders.js";
|
|
15
13
|
const WorkstreamStatusEnums = [
|
|
16
14
|
"RFP_DRAFT",
|
|
17
15
|
"PREWORK_RFC",
|
package/dist/style.css
CHANGED
|
@@ -380,9 +380,6 @@
|
|
|
380
380
|
.grid {
|
|
381
381
|
display: grid;
|
|
382
382
|
}
|
|
383
|
-
.hidden {
|
|
384
|
-
display: none;
|
|
385
|
-
}
|
|
386
383
|
.inline {
|
|
387
384
|
display: inline;
|
|
388
385
|
}
|
|
@@ -2597,6 +2594,9 @@
|
|
|
2597
2594
|
.mt-3 {
|
|
2598
2595
|
margin-top: calc(var(--spacing) * 3);
|
|
2599
2596
|
}
|
|
2597
|
+
.mb-0 {
|
|
2598
|
+
margin-bottom: calc(var(--spacing) * 0);
|
|
2599
|
+
}
|
|
2600
2600
|
.mb-2 {
|
|
2601
2601
|
margin-bottom: calc(var(--spacing) * 2);
|
|
2602
2602
|
}
|
|
@@ -3150,6 +3150,12 @@
|
|
|
3150
3150
|
--tw-tracking: var(--tracking-wider);
|
|
3151
3151
|
letter-spacing: var(--tracking-wider);
|
|
3152
3152
|
}
|
|
3153
|
+
.whitespace-pre-wrap {
|
|
3154
|
+
white-space: pre-wrap;
|
|
3155
|
+
}
|
|
3156
|
+
.text-amber-500 {
|
|
3157
|
+
color: var(--color-amber-500);
|
|
3158
|
+
}
|
|
3153
3159
|
.text-amber-600 {
|
|
3154
3160
|
color: var(--color-amber-600);
|
|
3155
3161
|
}
|
|
@@ -3376,6 +3382,13 @@
|
|
|
3376
3382
|
}
|
|
3377
3383
|
}
|
|
3378
3384
|
}
|
|
3385
|
+
.hover\:text-indigo-700 {
|
|
3386
|
+
&:hover {
|
|
3387
|
+
@media (hover: hover) {
|
|
3388
|
+
color: var(--color-indigo-700);
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3379
3392
|
.hover\:text-red-600 {
|
|
3380
3393
|
&:hover {
|
|
3381
3394
|
@media (hover: hover) {
|
|
@@ -3420,6 +3433,11 @@
|
|
|
3420
3433
|
border-color: var(--color-indigo-500);
|
|
3421
3434
|
}
|
|
3422
3435
|
}
|
|
3436
|
+
.focus\:border-red-500 {
|
|
3437
|
+
&:focus {
|
|
3438
|
+
border-color: var(--color-red-500);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3423
3441
|
.focus\:ring-2 {
|
|
3424
3442
|
&:focus {
|
|
3425
3443
|
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
|
@@ -3441,6 +3459,11 @@
|
|
|
3441
3459
|
--tw-ring-color: var(--color-indigo-500);
|
|
3442
3460
|
}
|
|
3443
3461
|
}
|
|
3462
|
+
.focus\:ring-red-500 {
|
|
3463
|
+
&:focus {
|
|
3464
|
+
--tw-ring-color: var(--color-red-500);
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3444
3467
|
.focus\:outline-none {
|
|
3445
3468
|
&:focus {
|
|
3446
3469
|
--tw-outline-style: none;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/builders-addon/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/builders-addon/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAe5D,eAAO,MAAM,YAAY,GAAI,UAAU,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAqbxE,CAAC"}
|
|
@@ -49,7 +49,7 @@ export const getResolvers = (subgraph) => {
|
|
|
49
49
|
String(filter.status || "").toLowerCase())
|
|
50
50
|
return false;
|
|
51
51
|
if (filter.skills && filter.skills.length > 0) {
|
|
52
|
-
const builderSkills = (builder.
|
|
52
|
+
const builderSkills = (builder.skills || []).map((s) => String(s).toLowerCase());
|
|
53
53
|
const hasAllSkills = filter.skills.every((skill) => builderSkills.includes(String(skill).toLowerCase()));
|
|
54
54
|
if (!hasAllSkills)
|
|
55
55
|
return false;
|
|
@@ -65,35 +65,120 @@ export const getResolvers = (subgraph) => {
|
|
|
65
65
|
return {
|
|
66
66
|
Query: {
|
|
67
67
|
builders: async (parent, args) => {
|
|
68
|
-
const filter = args.filter;
|
|
69
68
|
const drives = await getCandidateDrives();
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
const filter = args.filter;
|
|
70
|
+
let builderDocs = [];
|
|
72
71
|
const sowDocs = [];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
const allowedDriveIds = new Set(drives);
|
|
73
|
+
// Step 1: If networkSlug is provided, identify the network drive and valid builder PHIDs
|
|
74
|
+
if (filter?.networkSlug) {
|
|
75
|
+
allowedDriveIds.clear();
|
|
76
|
+
const targetNetworkSlug = filter.networkSlug.toLowerCase().trim();
|
|
77
|
+
let targetDriveId = null;
|
|
78
|
+
let builderPhids = [];
|
|
79
|
+
// Find the network drive matching the slug
|
|
80
|
+
for (const driveId of drives) {
|
|
81
|
+
try {
|
|
82
|
+
const docIds = await reactor.getDocuments(driveId);
|
|
83
|
+
const docs = await Promise.all(docIds.map(async (docId) => {
|
|
84
|
+
try {
|
|
85
|
+
return await reactor.getDocument(docId);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}));
|
|
91
|
+
const networkDoc = docs.find((doc) => {
|
|
92
|
+
if (!doc ||
|
|
93
|
+
doc.header.documentType !== "powerhouse/network-profile")
|
|
94
|
+
return false;
|
|
95
|
+
const state = doc.state.global;
|
|
96
|
+
if (!state?.name)
|
|
97
|
+
return false;
|
|
98
|
+
const slug = state.name
|
|
99
|
+
.toLowerCase()
|
|
100
|
+
.trim()
|
|
101
|
+
.split(/\s+/)
|
|
102
|
+
.join("-");
|
|
103
|
+
return slug === targetNetworkSlug;
|
|
104
|
+
});
|
|
105
|
+
if (networkDoc) {
|
|
106
|
+
targetDriveId = driveId;
|
|
107
|
+
// Get the builders list from this drive
|
|
108
|
+
const buildersDoc = docs.find((doc) => doc && doc.header.documentType === "powerhouse/builders");
|
|
109
|
+
if (buildersDoc) {
|
|
110
|
+
const state = buildersDoc.state.global;
|
|
111
|
+
if (Array.isArray(state.builders)) {
|
|
112
|
+
builderPhids = state.builders.filter((id) => typeof id === "string");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.warn(`Failed to inspect drive ${driveId}:`, error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (targetDriveId) {
|
|
123
|
+
allowedDriveIds.add(targetDriveId);
|
|
124
|
+
// Fetch SOWs from the network drive
|
|
125
|
+
try {
|
|
126
|
+
const docIds = await reactor.getDocuments(targetDriveId);
|
|
127
|
+
for (const docId of docIds) {
|
|
128
|
+
try {
|
|
129
|
+
const doc = await reactor.getDocument(docId);
|
|
130
|
+
if (doc.header.documentType === "powerhouse/scopeofwork") {
|
|
131
|
+
sowDocs.push(doc);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch { }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
console.warn(`Failed to fetch SOWs from drive ${targetDriveId}`, e);
|
|
139
|
+
}
|
|
140
|
+
// Fetch specific builder profiles
|
|
141
|
+
builderDocs = (await Promise.all(builderPhids.map(async (phid) => {
|
|
77
142
|
try {
|
|
78
|
-
|
|
143
|
+
const doc = await reactor.getDocument(phid);
|
|
144
|
+
return doc.header.documentType ===
|
|
145
|
+
"powerhouse/builder-profile"
|
|
146
|
+
? doc
|
|
147
|
+
: null;
|
|
79
148
|
}
|
|
80
149
|
catch {
|
|
81
150
|
return null;
|
|
82
151
|
}
|
|
83
|
-
}));
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
152
|
+
}))).filter((doc) => doc !== null);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
// Default behavior: Scan all drives
|
|
157
|
+
for (const driveId of drives) {
|
|
158
|
+
try {
|
|
159
|
+
const docIds = await reactor.getDocuments(driveId);
|
|
160
|
+
const docs = await Promise.all(docIds.map(async (docId) => {
|
|
161
|
+
try {
|
|
162
|
+
return await reactor.getDocument(docId);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}));
|
|
168
|
+
for (const doc of docs) {
|
|
169
|
+
if (!doc)
|
|
170
|
+
continue;
|
|
171
|
+
if (doc.header.documentType === "powerhouse/builder-profile") {
|
|
172
|
+
builderDocs.push(doc);
|
|
173
|
+
}
|
|
174
|
+
else if (doc.header.documentType === "powerhouse/scopeofwork") {
|
|
175
|
+
sowDocs.push(doc);
|
|
176
|
+
}
|
|
92
177
|
}
|
|
93
178
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
179
|
+
catch (error) {
|
|
180
|
+
console.warn(`Failed to process drive ${driveId}:`, error);
|
|
181
|
+
}
|
|
97
182
|
}
|
|
98
183
|
}
|
|
99
184
|
// Step 2: Build a map of deliverable OID -> deliverable object for each SOW
|
|
@@ -178,7 +263,8 @@ export const getResolvers = (subgraph) => {
|
|
|
178
263
|
project.scope.deliverableSetStatus ||
|
|
179
264
|
"DRAFT",
|
|
180
265
|
progress: project.scope.progress ?? null,
|
|
181
|
-
deliverablesCompleted: project.scope
|
|
266
|
+
deliverablesCompleted: project.scope
|
|
267
|
+
.deliverablesCompleted ?? {
|
|
182
268
|
total: 0,
|
|
183
269
|
completed: 0,
|
|
184
270
|
},
|
|
@@ -231,7 +317,7 @@ export const getResolvers = (subgraph) => {
|
|
|
231
317
|
const icon = String(state?.icon ?? "");
|
|
232
318
|
const description = String(state?.description ?? state?.slug ?? "");
|
|
233
319
|
const type = state?.type ?? "INDIVIDUAL";
|
|
234
|
-
const
|
|
320
|
+
const skills = Array.isArray(state?.skills) ? state.skills : [];
|
|
235
321
|
const scopes = Array.isArray(state?.scopes) ? state.scopes : [];
|
|
236
322
|
const links = Array.isArray(state?.links) ? state.links : [];
|
|
237
323
|
const contributors = Array.isArray(state?.contributors)
|
|
@@ -248,14 +334,20 @@ export const getResolvers = (subgraph) => {
|
|
|
248
334
|
type,
|
|
249
335
|
contributors,
|
|
250
336
|
status: state?.status ?? null,
|
|
251
|
-
|
|
337
|
+
skills,
|
|
252
338
|
scopes,
|
|
253
339
|
links,
|
|
254
340
|
projects: projectsByOwner.get(doc.header.id) || [],
|
|
255
341
|
};
|
|
256
342
|
return builder;
|
|
257
343
|
})
|
|
258
|
-
.filter((builder) =>
|
|
344
|
+
.filter((builder) => {
|
|
345
|
+
// Apply standard filters
|
|
346
|
+
if (!applyFilters(builder, filter)) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
return true;
|
|
350
|
+
});
|
|
259
351
|
return builders;
|
|
260
352
|
},
|
|
261
353
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../subgraphs/builders-addon/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,eAAO,MAAM,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../subgraphs/builders-addon/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,eAAO,MAAM,MAAM,EAAE,YA6MpB,CAAC"}
|
|
@@ -18,11 +18,9 @@ export const schema = gql `
|
|
|
18
18
|
status: BuilderStatus
|
|
19
19
|
skills: [BuilderSkill!]
|
|
20
20
|
scopes: [BuilderScope!]
|
|
21
|
+
networkSlug: String
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
24
|
## Builder Profile Schema
|
|
27
25
|
type BuilderProfileState {
|
|
28
26
|
id: PHID
|
|
@@ -35,7 +33,7 @@ export const schema = gql `
|
|
|
35
33
|
type: teamType!
|
|
36
34
|
contributors: [PHID!]!
|
|
37
35
|
status: BuilderStatus
|
|
38
|
-
|
|
36
|
+
skills: [BuilderSkill!]!
|
|
39
37
|
scopes: [BuilderScope!]!
|
|
40
38
|
links: [BuilderLink!]!
|
|
41
39
|
projects: [BuilderProject!]!
|
|
@@ -83,7 +81,7 @@ export const schema = gql `
|
|
|
83
81
|
label: String
|
|
84
82
|
}
|
|
85
83
|
|
|
86
|
-
type Builder {
|
|
84
|
+
type Builder @key(fields: "id") {
|
|
87
85
|
id: PHID
|
|
88
86
|
code: String
|
|
89
87
|
slug: String
|
|
@@ -94,7 +92,7 @@ export const schema = gql `
|
|
|
94
92
|
type: teamType!
|
|
95
93
|
contributors: [Builder!]!
|
|
96
94
|
status: BuilderStatus
|
|
97
|
-
|
|
95
|
+
skills: [BuilderSkill!]!
|
|
98
96
|
scopes: [BuilderScope!]!
|
|
99
97
|
links: [BuilderLink!]!
|
|
100
98
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/networks/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAK/D,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/networks/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAK/D,eAAO,MAAM,YAAY,GACvB,UAAU,YAAY,KACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAiNxB,CAAC"}
|
|
@@ -79,7 +79,8 @@ export const getResolvers = (subgraph) => {
|
|
|
79
79
|
}
|
|
80
80
|
}));
|
|
81
81
|
contributorDocs.forEach((doc) => {
|
|
82
|
-
if (doc &&
|
|
82
|
+
if (doc &&
|
|
83
|
+
doc.header.documentType === "powerhouse/builder-profile") {
|
|
83
84
|
builderProfileMap.set(doc.header.id, doc);
|
|
84
85
|
}
|
|
85
86
|
});
|
|
@@ -154,7 +155,8 @@ export const getResolvers = (subgraph) => {
|
|
|
154
155
|
Builder: {
|
|
155
156
|
contributors: (parent) => {
|
|
156
157
|
// Resolve contributor PHIDs to Builder objects
|
|
157
|
-
if (!parent._contributorPhids ||
|
|
158
|
+
if (!parent._contributorPhids ||
|
|
159
|
+
parent._contributorPhids.length === 0) {
|
|
158
160
|
return [];
|
|
159
161
|
}
|
|
160
162
|
if (!getBuilderProfileByPhid) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/workstreams/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAuC5D,eAAO,MAAM,YAAY,GAAI,UAAU,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"resolvers.d.ts","sourceRoot":"","sources":["../../../subgraphs/workstreams/resolvers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAuC5D,eAAO,MAAM,YAAY,GAAI,UAAU,SAAS,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CA27BxE,CAAC"}
|
|
@@ -171,8 +171,7 @@ export const getResolvers = (subgraph) => {
|
|
|
171
171
|
}
|
|
172
172
|
}));
|
|
173
173
|
nestedContributorDocs.forEach((doc) => {
|
|
174
|
-
if (doc &&
|
|
175
|
-
doc.header.documentType === "powerhouse/builder-profile") {
|
|
174
|
+
if (doc && doc.header.documentType === "powerhouse/builder-profile") {
|
|
176
175
|
builderProfileMap.set(doc.header.id, doc);
|
|
177
176
|
}
|
|
178
177
|
});
|
|
@@ -785,11 +784,13 @@ export const getResolvers = (subgraph) => {
|
|
|
785
784
|
SOW_Project: {
|
|
786
785
|
projectOwner: (parent) => {
|
|
787
786
|
// Handle null, undefined, or empty string cases
|
|
788
|
-
if (parent?.projectOwner === null ||
|
|
787
|
+
if (parent?.projectOwner === null ||
|
|
788
|
+
parent?.projectOwner === undefined) {
|
|
789
789
|
return null;
|
|
790
790
|
}
|
|
791
791
|
// Check if it's an empty string
|
|
792
|
-
if (typeof parent.projectOwner === "string" &&
|
|
792
|
+
if (typeof parent.projectOwner === "string" &&
|
|
793
|
+
parent.projectOwner.trim() === "") {
|
|
793
794
|
return null;
|
|
794
795
|
}
|
|
795
796
|
if (!getBuilderProfileByPhid)
|
|
@@ -803,7 +804,8 @@ export const getResolvers = (subgraph) => {
|
|
|
803
804
|
Builder: {
|
|
804
805
|
contributors: (parent) => {
|
|
805
806
|
// Resolve contributor PHIDs to Builder objects
|
|
806
|
-
if (!parent._contributorPhids ||
|
|
807
|
+
if (!parent._contributorPhids ||
|
|
808
|
+
parent._contributorPhids.length === 0) {
|
|
807
809
|
return [];
|
|
808
810
|
}
|
|
809
811
|
if (!getBuilderProfileByPhid) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../subgraphs/workstreams/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,eAAO,MAAM,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../subgraphs/workstreams/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,eAAO,MAAM,MAAM,EAAE,YAsgBpB,CAAC"}
|
|
@@ -21,7 +21,6 @@ export const schema = gql `
|
|
|
21
21
|
workstream_title: String
|
|
22
22
|
workstream_status: WorkstreamStatus
|
|
23
23
|
sow_phid: PHID
|
|
24
|
-
roadmap_oid: PHID
|
|
25
24
|
final_milestone_target: DateTime
|
|
26
25
|
initial_proposal_status: ProposalStatus
|
|
27
26
|
initial_proposal_author: PHID
|
|
@@ -458,7 +457,6 @@ export const schema = gql `
|
|
|
458
457
|
paymentTerms: PT_PaymentTermsState
|
|
459
458
|
}
|
|
460
459
|
|
|
461
|
-
|
|
462
460
|
# ==========================
|
|
463
461
|
# Builder (typed)
|
|
464
462
|
# ==========================
|
|
@@ -473,7 +471,7 @@ export const schema = gql `
|
|
|
473
471
|
type: teamType!
|
|
474
472
|
contributors: [Builder!]!
|
|
475
473
|
status: BuilderStatus
|
|
476
|
-
|
|
474
|
+
skills: [BuilderSkill!]!
|
|
477
475
|
scopes: [BuilderScope!]!
|
|
478
476
|
links: [BuilderLink!]!
|
|
479
477
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerhousedao/network-admin",
|
|
3
3
|
"description": "Network Admin package for Powerhouse",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.57",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"service-unstartup": "bash ./node_modules/@powerhousedao/ph-cli/dist/scripts/service-unstartup.sh"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@powerhousedao/builder-profile": "0.0.
|
|
65
|
+
"@powerhousedao/builder-profile": "0.0.9",
|
|
66
66
|
"@powerhousedao/builder-tools": "^5.0.12",
|
|
67
67
|
"@powerhousedao/common": "^5.0.12",
|
|
68
68
|
"@powerhousedao/design-system": "^5.0.12",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../../../editors/network-admin/components/builders.tsx"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,QAAQ,4CAQ/B"}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
export default function Builders() {
|
|
3
|
-
return (_jsx("div", { className: "w-full h-full p-6 overflow-auto", children: _jsx("div", { className: "max-w-7xl mx-auto", children: _jsx("h1", { className: "text-2xl font-bold", children: "Builders" }) }) }));
|
|
4
|
-
}
|