@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.
@@ -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
- onSetProfileName: (name: string) => void;
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, onSetProfileName, onSetSlug, onSetDescription, onSetLogo, onUpdateSocials, onClose, }: ProfileSectionProps): import("react/jsx-runtime").JSX.Element;
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
- export function ProfileSection({ profile, isEditing, onSetProfileName, onSetSlug, onSetDescription, onSetLogo, onUpdateSocials, onClose, }) {
4
- 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: profile.name, onChange: (e) => onSetProfileName(e.target.value), placeholder: "Enter your profile name", description: "Your public display name" }), _jsx(StringField, { name: "slug", label: "Slug", value: profile.slug, onChange: (e) => onSetSlug(e.target.value), placeholder: "your-slug", description: "Unique identifier for your profile (used in URLs)" }), _jsx(StringField, { name: "description", label: "Description", value: profile.description || "", onChange: (e) => onSetDescription(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: profile.logo || "", onChange: (e) => onSetLogo(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: profile.socials.github || "", onChange: (e) => onUpdateSocials({ ...profile.socials, github: e.target.value }), placeholder: "https://github.com/username" }), _jsx(UrlField, { name: "website", label: "Website", value: profile.socials.website || "", onChange: (e) => onUpdateSocials({ ...profile.socials, website: e.target.value }), placeholder: "https://your-website.com" }), _jsx(UrlField, { name: "x", label: "X (Twitter)", value: profile.socials.xProfile || "", onChange: (e) => onUpdateSocials({ ...profile.socials, 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: 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: "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" }))] })] })] })) })] }));
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, onSetProfileName: profileHandlers.handleSetProfileName, onSetSlug: profileHandlers.handleSetSlug, onSetDescription: profileHandlers.handleSetProfileDescription, onSetLogo: profileHandlers.handleSetLogo, onUpdateSocials: profileHandlers.handleUpdateSocials, 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) => {
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.33",
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",