@powerhousedao/vetra-builder-package 0.0.33 → 0.0.34
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/builder-team-editor/components/ProfileSection.d.ts +4 -10
- package/dist/editors/builder-team-editor/components/ProfileSection.js +66 -2
- package/dist/editors/builder-team-editor/editor.js +1 -1
- package/dist/scripts/upload-to-drive.d.ts +13 -0
- package/dist/scripts/upload-to-drive.js +261 -0
- package/package.json +4 -1
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import type { BuilderTeamDocument } from "../../../document-models/builder-team/index.js";
|
|
2
|
+
import { type Action } from "document-model";
|
|
3
|
+
import { type DocumentDispatch } from "@powerhousedao/reactor-browser";
|
|
2
4
|
interface ProfileSectionProps {
|
|
3
5
|
profile: BuilderTeamDocument['state']['global']['profile'];
|
|
4
6
|
isEditing: boolean;
|
|
5
|
-
|
|
6
|
-
onSetSlug: (slug: string) => void;
|
|
7
|
-
onSetDescription: (description: string) => void;
|
|
8
|
-
onSetLogo: (logoUrl: string) => void;
|
|
9
|
-
onUpdateSocials: (socials: {
|
|
10
|
-
github?: string | null;
|
|
11
|
-
website?: string | null;
|
|
12
|
-
x?: string | null;
|
|
13
|
-
}) => void;
|
|
7
|
+
dispatch: DocumentDispatch<Action>;
|
|
14
8
|
onClose: () => void;
|
|
15
9
|
}
|
|
16
|
-
export declare function ProfileSection({ profile, isEditing,
|
|
10
|
+
export declare function ProfileSection({ profile, isEditing, dispatch, onClose, }: ProfileSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
17
11
|
export {};
|
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
2
3
|
import { Button, Form, StringField, UrlField, Icon } from "@powerhousedao/document-engineering";
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
import { actions } from "../../../document-models/builder-team/index.js";
|
|
5
|
+
export function ProfileSection({ profile, isEditing, dispatch, onClose, }) {
|
|
6
|
+
// Local state for form fields
|
|
7
|
+
const [formData, setFormData] = useState({
|
|
8
|
+
name: profile.name,
|
|
9
|
+
slug: profile.slug,
|
|
10
|
+
description: profile.description || "",
|
|
11
|
+
logo: profile.logo || "",
|
|
12
|
+
github: profile.socials.github || "",
|
|
13
|
+
website: profile.socials.website || "",
|
|
14
|
+
x: profile.socials.xProfile || "",
|
|
15
|
+
});
|
|
16
|
+
// Reset form data when entering edit mode (not when profile changes during editing)
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (isEditing) {
|
|
19
|
+
setFormData({
|
|
20
|
+
name: profile.name,
|
|
21
|
+
slug: profile.slug,
|
|
22
|
+
description: profile.description || "",
|
|
23
|
+
logo: profile.logo || "",
|
|
24
|
+
github: profile.socials.github || "",
|
|
25
|
+
website: profile.socials.website || "",
|
|
26
|
+
x: profile.socials.xProfile || "",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
30
|
+
}, [isEditing]);
|
|
31
|
+
const handleSaveChanges = () => {
|
|
32
|
+
// Dispatch all changes when save is clicked
|
|
33
|
+
const nameTrimmed = formData.name.trim();
|
|
34
|
+
const slugTrimmed = formData.slug.trim();
|
|
35
|
+
const descriptionTrimmed = formData.description.trim();
|
|
36
|
+
const logoTrimmed = formData.logo.trim();
|
|
37
|
+
const githubTrimmed = formData.github.trim();
|
|
38
|
+
const websiteTrimmed = formData.website.trim();
|
|
39
|
+
const xTrimmed = formData.x.trim();
|
|
40
|
+
// Dispatch name change
|
|
41
|
+
if (nameTrimmed && nameTrimmed !== profile.name) {
|
|
42
|
+
dispatch(actions.setTeamName({ name: nameTrimmed }));
|
|
43
|
+
}
|
|
44
|
+
// Dispatch slug change
|
|
45
|
+
if (slugTrimmed && slugTrimmed !== profile.slug) {
|
|
46
|
+
dispatch(actions.setSlug({ slug: slugTrimmed }));
|
|
47
|
+
}
|
|
48
|
+
// Dispatch description change
|
|
49
|
+
if (descriptionTrimmed !== (profile.description || "")) {
|
|
50
|
+
dispatch(actions.setDescription({ description: descriptionTrimmed || null }));
|
|
51
|
+
}
|
|
52
|
+
// Dispatch logo change
|
|
53
|
+
if (logoTrimmed && logoTrimmed !== (profile.logo || "")) {
|
|
54
|
+
dispatch(actions.setLogo({ logo: logoTrimmed }));
|
|
55
|
+
}
|
|
56
|
+
// Dispatch socials change if any social field changed
|
|
57
|
+
if (githubTrimmed !== (profile.socials.github || "") ||
|
|
58
|
+
websiteTrimmed !== (profile.socials.website || "") ||
|
|
59
|
+
xTrimmed !== (profile.socials.xProfile || "")) {
|
|
60
|
+
dispatch(actions.setSocials({
|
|
61
|
+
github: githubTrimmed || null,
|
|
62
|
+
website: websiteTrimmed || null,
|
|
63
|
+
xProfile: xTrimmed || null,
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
onClose();
|
|
67
|
+
};
|
|
68
|
+
return (_jsxs("div", { className: "bg-white rounded-xl shadow-md border border-gray-200 overflow-hidden", children: [_jsx("div", { className: "px-6 py-5 border-b border-gray-200 bg-gray-50", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "People", size: "24px", color: "rgb(75 85 99)" }), _jsxs("div", { children: [_jsx("h2", { className: "text-2xl font-bold text-gray-900", children: "Profile Information" }), _jsx("p", { className: "text-sm text-gray-600 mt-0.5", children: "Manage your builder profile details" })] })] }) }), _jsx("div", { className: "p-6", children: isEditing ? (_jsx(Form, { onSubmit: (e) => e.preventDefault(), children: _jsxs("div", { className: "space-y-6", children: [_jsx(StringField, { name: "profileName", label: "Profile Name", value: formData.name, onChange: (e) => setFormData(prev => ({ ...prev, name: e.target.value })), placeholder: "Enter your profile name", description: "Your public display name" }), _jsx(StringField, { name: "slug", label: "Slug", value: formData.slug, onChange: (e) => setFormData(prev => ({ ...prev, slug: e.target.value })), placeholder: "your-slug", description: "Unique identifier for your profile (used in URLs)" }), _jsx(StringField, { name: "description", label: "Description", value: formData.description, onChange: (e) => setFormData(prev => ({ ...prev, description: e.target.value })), placeholder: "Tell us about yourself and your work", description: "Brief description of your work and interests" }), _jsx(UrlField, { name: "logo", label: "Logo URL", value: formData.logo, onChange: (e) => setFormData(prev => ({ ...prev, logo: e.target.value })), placeholder: "https://example.com/logo.png", description: "URL to your profile logo image" }), _jsxs("div", { className: "space-y-4 pt-4 border-t border-gray-200", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("svg", { className: "w-5 h-5 text-gray-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" }) }), _jsx("h3", { className: "text-lg font-semibold text-gray-900", children: "Social Links" })] }), _jsx(UrlField, { name: "github", label: "GitHub", value: formData.github, onChange: (e) => setFormData(prev => ({ ...prev, github: e.target.value })), placeholder: "https://github.com/username" }), _jsx(UrlField, { name: "website", label: "Website", value: formData.website, onChange: (e) => setFormData(prev => ({ ...prev, website: e.target.value })), placeholder: "https://your-website.com" }), _jsx(UrlField, { name: "x", label: "X (Twitter)", value: formData.x, onChange: (e) => setFormData(prev => ({ ...prev, x: e.target.value })), placeholder: "https://x.com/username" })] }), _jsxs("div", { className: "flex justify-end space-x-3 pt-4", children: [_jsx(Button, { color: "light", onClick: onClose, children: _jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }), "Cancel"] }) }), _jsx(Button, { onClick: handleSaveChanges, children: _jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }), "Save Changes"] }) })] })] }) })) : (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [_jsxs("div", { className: "bg-gray-50 p-4 rounded-lg border border-gray-200", children: [_jsx("label", { className: "block text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2", children: "Name" }), _jsx("p", { className: "text-base font-medium text-gray-900", children: profile.name || "Not set" })] }), _jsxs("div", { className: "bg-gray-50 p-4 rounded-lg border border-gray-200", children: [_jsx("label", { className: "block text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2", children: "Slug" }), _jsx("p", { className: "text-base font-medium text-gray-900", children: profile.slug ? `@${profile.slug}` : "Not set" })] })] }), profile.description && (_jsxs("div", { className: "bg-gray-50 p-4 rounded-lg border border-gray-200", children: [_jsx("label", { className: "block text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2", children: "Description" }), _jsx("p", { className: "text-base text-gray-900 leading-relaxed", children: profile.description })] })), _jsxs("div", { className: "bg-gray-50 p-4 rounded-lg border border-gray-200", children: [_jsx("label", { className: "block text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3", children: "Social Links" }), _jsxs("div", { className: "flex flex-wrap gap-3", children: [profile.socials.github && (_jsxs("a", { href: profile.socials.github, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-2 px-3 py-2 bg-white rounded-lg border border-gray-300 text-sm font-medium text-gray-700 hover:bg-gray-50 hover:border-gray-400 transition-colors", children: [_jsx("svg", { className: "w-4 h-4", fill: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { fillRule: "evenodd", d: "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z", clipRule: "evenodd" }) }), "GitHub"] })), profile.socials.website && (_jsxs("a", { href: profile.socials.website, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-2 px-3 py-2 bg-white rounded-lg border border-gray-300 text-sm font-medium text-gray-700 hover:bg-gray-50 hover:border-gray-400 transition-colors", children: [_jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" }) }), "Website"] })), profile.socials.xProfile && (_jsxs("a", { href: profile.socials.xProfile, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-2 px-3 py-2 bg-white rounded-lg border border-gray-300 text-sm font-medium text-gray-700 hover:bg-gray-50 hover:border-gray-400 transition-colors", children: [_jsx("svg", { className: "w-4 h-4", fill: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" }) }), "X (Twitter)"] })), !profile.socials.github && !profile.socials.website && !profile.socials.xProfile && (_jsx("span", { className: "text-gray-500 text-sm italic", children: "No social links added" }))] })] })] })) })] }));
|
|
5
69
|
}
|
|
@@ -63,7 +63,7 @@ export function Editor(props) {
|
|
|
63
63
|
const handleReorderPackages = (spaceId, packageIds, targetIndex) => {
|
|
64
64
|
dispatch(actions.reorderPackages({ spaceId, packageIds, targetIndex }));
|
|
65
65
|
};
|
|
66
|
-
return (_jsxs("div", { className: "html-defaults-container min-h-screen bg-gray-50", children: [_jsx(Header, { profile: profile, isEditingProfile: isEditingProfile, onToggleEdit: () => setIsEditingProfile(!isEditingProfile) }), _jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8", children: _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-8", children: [_jsxs("div", { className: "lg:col-span-2 space-y-8", children: [_jsx(ProfileSection, { profile: profile, isEditing: isEditingProfile,
|
|
66
|
+
return (_jsxs("div", { className: "html-defaults-container min-h-screen bg-gray-50", children: [_jsx(Header, { profile: profile, isEditingProfile: isEditingProfile, onToggleEdit: () => setIsEditingProfile(!isEditingProfile) }), _jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8", children: _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-3 gap-8", children: [_jsxs("div", { className: "lg:col-span-2 space-y-8", children: [_jsx(ProfileSection, { profile: profile, isEditing: isEditingProfile, dispatch: dispatch, onClose: () => setIsEditingProfile(false) }), _jsx(SpacesSection, { spaces: spaces, editingSpaceId: spaceHandlers.editingSpaceId, editingSpaceTitle: spaceHandlers.editingSpaceTitle, editingSpaceDescription: spaceHandlers.editingSpaceDescription, editingPackageId: packageHandlers.editingPackageId, onAddSpace: spaceHandlers.handleAddSpace, onDeleteSpace: spaceHandlers.handleDeleteSpace, onStartEditingSpace: spaceHandlers.handleStartEditingSpace, onSaveSpaceEdit: spaceHandlers.handleSaveSpaceEdit, onCancelSpaceEdit: spaceHandlers.handleCancelSpaceEdit, onSetEditingSpaceTitle: spaceHandlers.setEditingSpaceTitle, onSetEditingSpaceDescription: spaceHandlers.setEditingSpaceDescription, onAddPackageToSpace: handleAddPackageToSpace, onEditPackage: packageHandlers.handleStartEditingPackage, onDeletePackage: packageHandlers.handleDeletePackage, onSavePackage: (packageInfo) => {
|
|
67
67
|
// Update the package with info
|
|
68
68
|
dispatch(actions.updatePackageInfo({
|
|
69
69
|
id: packageInfo.id,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Upload Powerhouse Document to Remote Drive via Switchboard GraphQL API
|
|
4
|
+
*
|
|
5
|
+
* Usage (from project root):
|
|
6
|
+
* bun run scripts/upload-to-drive.ts <document-path> <drive-url>
|
|
7
|
+
* OR
|
|
8
|
+
* npx tsx scripts/upload-to-drive.ts <document-path> <drive-url>
|
|
9
|
+
*
|
|
10
|
+
* Example:
|
|
11
|
+
* bun run scripts/upload-to-drive.ts /home/froid/Dokumente/SkyFintech.phvba.phd https://switchboard.vetra.io/d/sky-fintech
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Upload Powerhouse Document to Remote Drive via Switchboard GraphQL API
|
|
4
|
+
*
|
|
5
|
+
* Usage (from project root):
|
|
6
|
+
* bun run scripts/upload-to-drive.ts <document-path> <drive-url>
|
|
7
|
+
* OR
|
|
8
|
+
* npx tsx scripts/upload-to-drive.ts <document-path> <drive-url>
|
|
9
|
+
*
|
|
10
|
+
* Example:
|
|
11
|
+
* bun run scripts/upload-to-drive.ts /home/froid/Dokumente/SkyFintech.phvba.phd https://switchboard.vetra.io/d/sky-fintech
|
|
12
|
+
*/
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import path from 'path';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
import AdmZip from 'adm-zip';
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = path.dirname(__filename);
|
|
19
|
+
/**
|
|
20
|
+
* Extract JSON files from .phd (ZIP) file
|
|
21
|
+
*/
|
|
22
|
+
function extractDocumentData(filePath) {
|
|
23
|
+
console.log(`📦 Extracting document: ${filePath}`);
|
|
24
|
+
const zip = new AdmZip(filePath);
|
|
25
|
+
const zipEntries = zip.getEntries();
|
|
26
|
+
let header = null;
|
|
27
|
+
let operations = null;
|
|
28
|
+
let state = null;
|
|
29
|
+
let currentState = null;
|
|
30
|
+
zipEntries.forEach((entry) => {
|
|
31
|
+
const content = entry.getData().toString('utf8');
|
|
32
|
+
switch (entry.entryName) {
|
|
33
|
+
case 'header.json':
|
|
34
|
+
header = JSON.parse(content);
|
|
35
|
+
console.log(` ✓ header.json: ${header?.id}`);
|
|
36
|
+
break;
|
|
37
|
+
case 'operations.json':
|
|
38
|
+
operations = JSON.parse(content);
|
|
39
|
+
console.log(` ✓ operations.json: ${operations?.global.length} global ops, ${operations?.local.length} local ops`);
|
|
40
|
+
break;
|
|
41
|
+
case 'state.json':
|
|
42
|
+
state = JSON.parse(content);
|
|
43
|
+
console.log(` ✓ state.json`);
|
|
44
|
+
break;
|
|
45
|
+
case 'current-state.json':
|
|
46
|
+
currentState = JSON.parse(content);
|
|
47
|
+
console.log(` ✓ current-state.json`);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
if (!header || !operations) {
|
|
52
|
+
throw new Error('Missing required files in .phd document');
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
header: header,
|
|
56
|
+
operations: operations,
|
|
57
|
+
state: state || { global: {}, local: {} },
|
|
58
|
+
currentState: currentState || { global: {}, local: {} },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Extract drive ID from drive URL
|
|
63
|
+
*/
|
|
64
|
+
function extractDriveId(driveUrl) {
|
|
65
|
+
const match = driveUrl.match(/\/d\/([^/]+)/);
|
|
66
|
+
if (!match) {
|
|
67
|
+
throw new Error(`Invalid drive URL: ${driveUrl}`);
|
|
68
|
+
}
|
|
69
|
+
return match[1];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create document via Switchboard GraphQL API
|
|
73
|
+
*/
|
|
74
|
+
async function createDocument(driveUrl, header, initialState) {
|
|
75
|
+
const graphqlUrl = driveUrl.replace(/\/d\/[^/]+$/, '/graphql');
|
|
76
|
+
const driveId = extractDriveId(driveUrl);
|
|
77
|
+
console.log(`\n📝 Creating document on Switchboard...`);
|
|
78
|
+
console.log(` GraphQL endpoint: ${graphqlUrl}`);
|
|
79
|
+
console.log(` Drive ID: ${driveId}`);
|
|
80
|
+
console.log(` Document Type: ${header.documentType}`);
|
|
81
|
+
const mutation = `
|
|
82
|
+
mutation BuilderTeam_createDocument($name: String!, $driveId: String!) {
|
|
83
|
+
BuilderTeam_createDocument(name: $name, driveId: $driveId)
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
const variables = {
|
|
87
|
+
name: header.name || header.slug || 'Untitled',
|
|
88
|
+
driveId: driveId,
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
const response = await fetch(graphqlUrl, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
},
|
|
96
|
+
body: JSON.stringify({
|
|
97
|
+
query: mutation,
|
|
98
|
+
variables,
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
const result = await response.json();
|
|
102
|
+
if (result.errors) {
|
|
103
|
+
console.error(` ✗ GraphQL errors:`, JSON.stringify(result.errors, null, 2));
|
|
104
|
+
throw new Error('Failed to create document');
|
|
105
|
+
}
|
|
106
|
+
const documentId = result.data?.BuilderTeam_createDocument;
|
|
107
|
+
console.log(` ✓ Document created successfully`);
|
|
108
|
+
console.log(` Document ID (PHID): ${documentId}`);
|
|
109
|
+
return documentId;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error(` ✗ Error creating document:`, error);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Map operation types to GraphQL mutation names
|
|
118
|
+
*/
|
|
119
|
+
const OPERATION_MUTATION_MAP = {
|
|
120
|
+
SET_LOGO: 'BuilderTeam_setLogo',
|
|
121
|
+
SET_TEAM_NAME: 'BuilderTeam_setTeamName',
|
|
122
|
+
SET_SLUG: 'BuilderTeam_setSlug',
|
|
123
|
+
SET_DESCRIPTION: 'BuilderTeam_setDescription',
|
|
124
|
+
SET_SOCIALS: 'BuilderTeam_setSocials',
|
|
125
|
+
ADD_MEMBER: 'BuilderTeam_addMember',
|
|
126
|
+
UPDATE_MEMBER_INFO: 'BuilderTeam_updateMemberInfo',
|
|
127
|
+
REMOVE_MEMBER: 'BuilderTeam_removeMember',
|
|
128
|
+
ADD_SPACE: 'BuilderTeam_addSpace',
|
|
129
|
+
UPDATE_SPACE_INFO: 'BuilderTeam_updateSpaceInfo',
|
|
130
|
+
REMOVE_SPACE: 'BuilderTeam_removeSpace',
|
|
131
|
+
REORDER_SPACES: 'BuilderTeam_reorderSpaces',
|
|
132
|
+
ADD_PACKAGE: 'BuilderTeam_addPackage',
|
|
133
|
+
UPDATE_PACKAGE_INFO: 'BuilderTeam_updatePackageInfo',
|
|
134
|
+
REMOVE_PACKAGE: 'BuilderTeam_removePackage',
|
|
135
|
+
REORDER_PACKAGES: 'BuilderTeam_reorderPackages',
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Convert operation type to input type name
|
|
139
|
+
*/
|
|
140
|
+
function getInputTypeName(operationType) {
|
|
141
|
+
// Convert SET_TEAM_NAME -> SetTeamNameInput
|
|
142
|
+
const parts = operationType.split('_');
|
|
143
|
+
const camelCase = parts.map(p => p.charAt(0) + p.slice(1).toLowerCase()).join('');
|
|
144
|
+
return `BuilderTeam_${camelCase}Input`;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Apply a single operation via its specific mutation
|
|
148
|
+
*/
|
|
149
|
+
async function applyOperation(graphqlUrl, driveId, docId, operation) {
|
|
150
|
+
const mutationName = OPERATION_MUTATION_MAP[operation.type];
|
|
151
|
+
if (!mutationName) {
|
|
152
|
+
console.log(` ⚠ Skipping unknown operation: ${operation.type}`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const inputTypeName = getInputTypeName(operation.type);
|
|
156
|
+
const mutation = `
|
|
157
|
+
mutation Apply($driveId: String, $docId: PHID, $input: ${inputTypeName}) {
|
|
158
|
+
${mutationName}(driveId: $driveId, docId: $docId, input: $input)
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
const variables = {
|
|
162
|
+
driveId,
|
|
163
|
+
docId,
|
|
164
|
+
input: operation.input,
|
|
165
|
+
};
|
|
166
|
+
const response = await fetch(graphqlUrl, {
|
|
167
|
+
method: 'POST',
|
|
168
|
+
headers: {
|
|
169
|
+
'Content-Type': 'application/json',
|
|
170
|
+
},
|
|
171
|
+
body: JSON.stringify({
|
|
172
|
+
query: mutation,
|
|
173
|
+
variables,
|
|
174
|
+
}),
|
|
175
|
+
});
|
|
176
|
+
const result = await response.json();
|
|
177
|
+
if (result.errors) {
|
|
178
|
+
throw new Error(`GraphQL error: ${JSON.stringify(result.errors)}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Apply operations to document via Switchboard GraphQL API
|
|
183
|
+
*/
|
|
184
|
+
async function applyOperations(driveUrl, documentId, operations) {
|
|
185
|
+
const graphqlUrl = driveUrl.replace(/\/d\/[^/]+$/, '/graphql');
|
|
186
|
+
const driveId = extractDriveId(driveUrl);
|
|
187
|
+
console.log(`\n⚙️ Applying ${operations.length} operations...`);
|
|
188
|
+
let successCount = 0;
|
|
189
|
+
let skipCount = 0;
|
|
190
|
+
let errorCount = 0;
|
|
191
|
+
for (let i = 0; i < operations.length; i++) {
|
|
192
|
+
const operation = operations[i];
|
|
193
|
+
const progress = `[${i + 1}/${operations.length}]`;
|
|
194
|
+
try {
|
|
195
|
+
await applyOperation(graphqlUrl, driveId, documentId, operation);
|
|
196
|
+
console.log(` ${progress} ✓ ${operation.type}`);
|
|
197
|
+
successCount++;
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
if (OPERATION_MUTATION_MAP[operation.type]) {
|
|
201
|
+
console.error(` ${progress} ✗ ${operation.type}: ${error instanceof Error ? error.message : String(error)}`);
|
|
202
|
+
errorCount++;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
console.log(` ${progress} ⚠ Skipped ${operation.type} (no mutation)`);
|
|
206
|
+
skipCount++;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
console.log(`\n Summary:`);
|
|
211
|
+
console.log(` ✓ Success: ${successCount}`);
|
|
212
|
+
if (skipCount > 0)
|
|
213
|
+
console.log(` ⚠ Skipped: ${skipCount}`);
|
|
214
|
+
if (errorCount > 0)
|
|
215
|
+
console.log(` ✗ Errors: ${errorCount}`);
|
|
216
|
+
if (errorCount > 0) {
|
|
217
|
+
throw new Error(`Failed to apply ${errorCount} operations`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Main function
|
|
222
|
+
*/
|
|
223
|
+
async function main() {
|
|
224
|
+
console.log('=== Powerhouse Document Upload to Remote Drive ===\n');
|
|
225
|
+
const args = process.argv.slice(2);
|
|
226
|
+
if (args.length < 2) {
|
|
227
|
+
console.error('Usage: bun run scripts/upload-to-drive.ts <document-path> <drive-url>');
|
|
228
|
+
console.error('\nExample:');
|
|
229
|
+
console.error(' bun run scripts/upload-to-drive.ts /home/froid/Dokumente/SkyFintech.phvba.phd https://switchboard.vetra.io/d/sky-fintech');
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
const [documentPath, driveUrl] = args;
|
|
233
|
+
if (!fs.existsSync(documentPath)) {
|
|
234
|
+
console.error(`Error: Document not found: ${documentPath}`);
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
// Step 1: Extract document data
|
|
239
|
+
const { header, operations, state } = extractDocumentData(documentPath);
|
|
240
|
+
// Step 2: Create document
|
|
241
|
+
const documentId = await createDocument(driveUrl, header, state);
|
|
242
|
+
// Step 3: Apply operations
|
|
243
|
+
if (operations.global.length > 0) {
|
|
244
|
+
await applyOperations(driveUrl, documentId, operations.global);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
console.log('\n⚙️ No operations to apply');
|
|
248
|
+
}
|
|
249
|
+
console.log('\n=== Upload Complete ===');
|
|
250
|
+
console.log(`\nDocument uploaded to: ${driveUrl}`);
|
|
251
|
+
console.log(`Document ID (PHID): ${documentId}`);
|
|
252
|
+
console.log(`Original ID: ${header.id}`);
|
|
253
|
+
console.log(`Total operations: ${operations.global.length}`);
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
console.error('\n✗ Error:', error instanceof Error ? error.message : String(error));
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Run the script
|
|
261
|
+
main().catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerhousedao/vetra-builder-package",
|
|
3
3
|
"description": "",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.34",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -92,9 +92,11 @@
|
|
|
92
92
|
"@semantic-release/git": "^10.0.1",
|
|
93
93
|
"@tailwindcss/cli": "^4.0.15",
|
|
94
94
|
"@testing-library/react": "^16.3.0",
|
|
95
|
+
"@types/adm-zip": "^0.5.7",
|
|
95
96
|
"@types/node": "^22.13.11",
|
|
96
97
|
"@types/react": "^18.3.19",
|
|
97
98
|
"@vitejs/plugin-react": "^5.0.4",
|
|
99
|
+
"adm-zip": "^0.5.16",
|
|
98
100
|
"document-drive": "5.0.0-staging.30",
|
|
99
101
|
"eslint": "^9.22.0",
|
|
100
102
|
"eslint-plugin-react": "^7.37.4",
|
|
@@ -106,6 +108,7 @@
|
|
|
106
108
|
"react-dom": "^18.3.1",
|
|
107
109
|
"semantic-release": "^24.2.8",
|
|
108
110
|
"tailwindcss": "^4.1.4",
|
|
111
|
+
"tsx": "^4.20.6",
|
|
109
112
|
"typescript": "^5.8.3",
|
|
110
113
|
"typescript-eslint": "^8.30.1",
|
|
111
114
|
"vite": "^6.2.3",
|