BigBlocks Docs
Components/Profiles

ProfileManager

Complete profile management interface for viewing, editing, publishing, and switching between BAP profiles

A comprehensive profile management interface that provides a tabbed interface for viewing, editing, publishing, and switching between multiple BAP (Bitcoin Attestation Protocol) profiles.

Installation

npx bigblocks add profile-manager

Import

import { ProfileManager } from 'bigblocks';

Props

PropTypeRequiredDefaultDescription
userAuthUserNo-Current user with profiles
onProfileUpdate(profile: ProfileInfo) => voidNo-Profile update handler
onProfilePublish(profileId: string) => Promise<{ txid: string }>No-Publish profile to blockchain
onProfileCreate() => Promise<ProfileInfo>No-Create new profile handler
onProfileSwitch(profileId: string) => voidNo-Switch active profile handler
showPublisherbooleanNotrueShow publish tab
maxProfilesnumberNo5Maximum number of profiles
classNamestringNo-Additional CSS classes
enableProfileSyncbooleanNofalseEnable profile synchronization
maxDiscoveryAttemptsnumberNo3Max profile discovery attempts

Basic Usage

import { ProfileManager } from 'bigblocks';
import { useAuth } from 'bigblocks';

export default function ProfileSettings() {
  const { user } = useAuth();

  return (
    <div className="max-w-4xl mx-auto p-6">
      <h1 className="text-2xl font-bold mb-6">Profile Management</h1>
      
      <ProfileManager
        user={user}
        onProfileUpdate={(profile) => {
          console.log('Profile updated:', profile);
        }}
        onProfilePublish={async (profileId) => {
          console.log('Publishing profile:', profileId);
          // Return transaction ID after publishing
          return { txid: 'abc123...' };
        }}
        onProfileSwitch={(profileId) => {
          console.log('Switched to profile:', profileId);
        }}
      />
    </div>
  );
}

Advanced Usage

Complete Profile Management System

import { ProfileManager } from 'bigblocks';
import { useState, useEffect } from 'react';

export default function AdvancedProfileManager() {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const loadUser = async () => {
      try {
        const userData = await fetchCurrentUser();
        setUser(userData);
      } catch (error) {
        console.error('Failed to load user:', error);
      } finally {
        setLoading(false);
      }
    };

    loadUser();
  }, []);

  const handleProfileUpdate = async (profile) => {
    try {
      // Update profile in backend
      await updateProfile(profile.id, profile);
      
      // Update local user state
      setUser(prev => ({
        ...prev,
        profiles: prev.profiles.map(p => 
          p.id === profile.id ? profile : p
        )
      }));
      
      toast.success('Profile updated successfully!');
    } catch (error) {
      toast.error('Failed to update profile');
    }
  };

  const handleProfilePublish = async (profileId) => {
    try {
      // Publish to blockchain
      const result = await publishProfile(profileId);
      
      // Update published status
      setUser(prev => ({
        ...prev,
        profiles: prev.profiles.map(p => 
          p.id === profileId 
            ? { ...p, isPublished: true }
            : p
        )
      }));
      
      toast.success(`Profile published! TX: ${result.txid}`);
      return result;
    } catch (error) {
      toast.error('Failed to publish profile');
      throw error;
    }
  };

  const handleProfileCreate = async () => {
    try {
      const newProfile = await createNewProfile();
      
      setUser(prev => ({
        ...prev,
        profiles: [...prev.profiles, newProfile]
      }));
      
      toast.success('New profile created!');
      return newProfile;
    } catch (error) {
      toast.error('Failed to create profile');
      throw error;
    }
  };

  const handleProfileSwitch = async (profileId) => {
    try {
      await switchActiveProfile(profileId);
      
      setUser(prev => ({
        ...prev,
        activeProfileId: profileId
      }));
      
      toast.success('Profile switched!');
    } catch (error) {
      toast.error('Failed to switch profile');
    }
  };

  if (loading) {
    return <div>Loading profile manager...</div>;
  }

  return (
    <ProfileManager
      user={user}
      onProfileUpdate={handleProfileUpdate}
      onProfilePublish={handleProfilePublish}
      onProfileCreate={handleProfileCreate}
      onProfileSwitch={handleProfileSwitch}
      showPublisher={true}
      maxProfiles={10}
      enableProfileSync={true}
      maxDiscoveryAttempts={5}
    />
  );
}

Organization Profile Management

import { ProfileManager } from 'bigblocks';

export default function OrganizationProfileManager({ organization }) {
  const handleOrgProfileUpdate = async (profile) => {
    // Validate organization-specific fields
    if (!profile.legalName || !profile.address) {
      toast.error('Organization profiles require legal name and address');
      return;
    }

    try {
      await updateOrganizationProfile(profile.id, profile);
      toast.success('Organization profile updated!');
    } catch (error) {
      toast.error('Failed to update organization profile');
    }
  };

  const handleOrgProfilePublish = async (profileId) => {
    try {
      // Organization profiles require additional verification
      const verification = await verifyOrganization(profileId);
      
      if (!verification.verified) {
        toast.error('Organization verification required before publishing');
        throw new Error('Verification required');
      }

      const result = await publishOrganizationProfile(profileId);
      toast.success('Organization profile published and verified!');
      return result;
    } catch (error) {
      toast.error('Failed to publish organization profile');
      throw error;
    }
  };

  return (
    <div>
      <h2 className="text-xl font-semibold mb-4">Organization Profiles</h2>
      
      <ProfileManager
        user={organization}
        onProfileUpdate={handleOrgProfileUpdate}
        onProfilePublish={handleOrgProfilePublish}
        showPublisher={true}
        maxProfiles={3} // Limit for organizations
        className="border-2 border-blue-200 rounded-lg"
      />
    </div>
  );
}

Multi-tenant Profile Management

import { ProfileManager } from 'bigblocks';

export default function MultiTenantProfileManager({ tenantId }) {
  const [users, setUsers] = useState([]);
  const [selectedUser, setSelectedUser] = useState(null);

  useEffect(() => {
    const loadTenantUsers = async () => {
      const tenantUsers = await fetchTenantUsers(tenantId);
      setUsers(tenantUsers);
      setSelectedUser(tenantUsers[0]);
    };

    loadTenantUsers();
  }, [tenantId]);

  const handleBulkProfileUpdate = async (profile) => {
    try {
      await updateTenantProfile(tenantId, profile.id, profile);
      
      // Update local state
      setUsers(prev => prev.map(user => ({
        ...user,
        profiles: user.profiles.map(p => 
          p.id === profile.id ? profile : p
        )
      })));
      
      toast.success('Profile updated across tenant!');
    } catch (error) {
      toast.error('Failed to update tenant profile');
    }
  };

  return (
    <div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
      {/* User selector */}
      <div className="lg:col-span-1">
        <h3 className="font-semibold mb-3">Select User</h3>
        <div className="space-y-2">
          {users.map(user => (
            <button
              key={user.id}
              onClick={() => setSelectedUser(user)}
              className={`w-full text-left p-3 rounded ${
                selectedUser?.id === user.id
                  ? 'bg-blue-100 text-blue-700'
                  : 'bg-gray-50 hover:bg-gray-100'
              }`}
            >
              {user.profiles[0]?.name || user.address}
            </button>
          ))}
        </div>
      </div>

      {/* Profile manager */}
      <div className="lg:col-span-3">
        {selectedUser && (
          <ProfileManager
            key={selectedUser.id}
            user={selectedUser}
            onProfileUpdate={handleBulkProfileUpdate}
            showPublisher={false} // Disable for tenant management
            maxProfiles={3}
          />
        )}
      </div>
    </div>
  );
}

Profile Manager with Sync

import { ProfileManager } from 'bigblocks';

export default function SyncedProfileManager() {
  const [user, setUser] = useState(null);
  const [syncStatus, setSyncStatus] = useState('idle');

  useEffect(() => {
    // Subscribe to profile sync events
    const unsubscribe = subscribeToProfileSync((event) => {
      switch (event.type) {
        case 'sync_started':
          setSyncStatus('syncing');
          break;
        case 'sync_completed':
          setSyncStatus('completed');
          // Reload user profiles
          loadUserProfiles();
          break;
        case 'sync_failed':
          setSyncStatus('failed');
          toast.error('Profile sync failed');
          break;
      }
    });

    return unsubscribe;
  }, []);

  const loadUserProfiles = async () => {
    try {
      const userData = await fetchUserWithProfiles();
      setUser(userData);
    } catch (error) {
      console.error('Failed to load user profiles:', error);
    }
  };

  return (
    <div>
      {syncStatus === 'syncing' && (
        <div className="mb-4 p-3 bg-blue-100 text-blue-700 rounded">
          Syncing profiles across devices...
        </div>
      )}
      
      {syncStatus === 'failed' && (
        <div className="mb-4 p-3 bg-red-100 text-red-700 rounded">
          Profile sync failed. Some changes may not be reflected.
        </div>
      )}
      
      <ProfileManager
        user={user}
        enableProfileSync={true}
        maxDiscoveryAttempts={5}
        onProfileUpdate={async (profile) => {
          await updateProfile(profile.id, profile);
          // Trigger sync after update
          await triggerProfileSync();
        }}
      />
    </div>
  );
}

Tab Interface

The ProfileManager provides a tabbed interface with the following tabs:

View Tab

  • Displays current profile information
  • Shows publication status
  • Profile address and details
  • Avatar and basic information

Edit Tab

  • Profile editing form
  • Real-time validation
  • Image URL input with preview
  • Save/cancel actions

Publish Tab (if showPublisher is true)

  • Blockchain publishing interface
  • Transaction status
  • Publication confirmation
  • TXID display after successful publish

Switch Tab

  • List of all user profiles
  • Active profile indicator
  • Switch profile actions
  • Profile creation option

Profile Creation

When creating new profiles:

const handleProfileCreate = async () => {
  try {
    // Generate new profile
    const newProfile = {
      id: generateProfileId(),
      address: generateNewAddress(),
      isPublished: false,
      name: '',
      description: '',
      image: ''
    };

    // Save to backend
    const savedProfile = await createProfile(newProfile);
    
    return savedProfile;
  } catch (error) {
    console.error('Profile creation failed:', error);
    throw error;
  }
};

Profile Discovery

When enableProfileSync is true, the component will attempt to discover profiles across devices:

const discoverProfiles = async (maxAttempts = 3) => {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      const discoveredProfiles = await searchBlockchainProfiles(user.address);
      
      if (discoveredProfiles.length > 0) {
        // Merge with existing profiles
        await mergeDiscoveredProfiles(discoveredProfiles);
        break;
      }
    } catch (error) {
      if (attempt === maxAttempts) {
        console.error('Profile discovery failed after', maxAttempts, 'attempts');
      }
    }
  }
};

Common Patterns

Profile Management Dashboard

import { ProfileManager, WalletOverview } from 'bigblocks';

export default function UserDashboard() {
  const { user } = useAuth();

  return (
    <div className="min-h-screen bg-gray-50">
      <div className="max-w-6xl mx-auto py-8 px-4">
        <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
          {/* Profile management */}
          <div className="lg:col-span-2">
            <ProfileManager
              user={user}
              onProfileUpdate={handleProfileUpdate}
              onProfilePublish={handleProfilePublish}
            />
          </div>

          {/* Wallet sidebar */}
          <div className="lg:col-span-1">
            <WalletOverview address={user?.address} />
          </div>
        </div>
      </div>
    </div>
  );
}

Mobile Profile Manager

import { ProfileManager } from 'bigblocks';

export default function MobileProfileManager({ user }) {
  return (
    <div className="min-h-screen bg-white">
      <div className="px-4 py-6">
        <h1 className="text-xl font-bold mb-4">Profile Settings</h1>
        
        <ProfileManager
          user={user}
          className="w-full"
          showPublisher={true}
          maxProfiles={3} // Limit for mobile
          onProfileUpdate={handleMobileProfileUpdate}
        />
      </div>
    </div>
  );
}

Admin Profile Manager

import { ProfileManager } from 'bigblocks';

export default function AdminProfileManager({ userToManage }) {
  const { user: adminUser } = useAuth();
  
  if (!adminUser?.isAdmin) {
    return <div>Access denied</div>;
  }

  return (
    <div>
      <div className="mb-6 p-4 bg-yellow-100 text-yellow-800 rounded">
        <strong>Admin Mode:</strong> Managing profiles for {userToManage.address}
      </div>
      
      <ProfileManager
        user={userToManage}
        onProfileUpdate={async (profile) => {
          // Admin update with audit log
          await adminUpdateProfile(profile.id, profile, adminUser.id);
        }}
        onProfilePublish={async (profileId) => {
          // Admin publish with approval
          const result = await adminPublishProfile(profileId, adminUser.id);
          return result;
        }}
        showPublisher={true}
        maxProfiles={10}
      />
    </div>
  );
}

User Data Structure

interface AuthUser {
  id: string;
  address: string;
  idKey: string;
  profiles: ProfileInfo[];
  activeProfileId: string;
  ordinalsAddress?: string;
  identityAddress?: string;
}

interface ProfileInfo {
  id: string;
  address: string;
  isPublished: boolean;
  name?: string;
  description?: string;
  image?: string;
  // ... other profile fields
}

Features

  • Tabbed Interface: Organized tabs for view, edit, publish, and switch operations
  • Profile Creation: Create new profiles with validation
  • Profile Switching: Switch between multiple profiles seamlessly
  • Blockchain Publishing: Publish profiles to Bitcoin blockchain
  • Profile Synchronization: Sync profiles across devices (optional)
  • Profile Discovery: Discover existing profiles on blockchain
  • Responsive Design: Works on desktop and mobile devices
  • Real-time Updates: Live updates when profiles change

Publishing Process

  1. Validation: Ensure profile has required fields
  2. Transaction Creation: Create Bitcoin transaction with profile data
  3. Signing: Sign transaction with profile's private key
  4. Broadcasting: Broadcast transaction to Bitcoin network
  5. Confirmation: Wait for transaction confirmation
  6. Status Update: Update profile publication status

Synchronization

When enableProfileSync is enabled:

  • Profiles are synced across devices
  • Changes are propagated to all connected devices
  • Conflict resolution for simultaneous edits
  • Backup and restore functionality

Error Handling

The component handles various error scenarios:

  • Network connectivity issues
  • Invalid profile data
  • Publishing failures
  • Sync conflicts
  • Validation errors

Best Practices

  1. Profile Limits: Set reasonable maxProfiles limits
  2. Validation: Validate profile data before saving
  3. Error Feedback: Provide clear error messages
  4. Loading States: Show loading indicators for async operations
  5. Confirmation: Confirm destructive actions like publishing

Troubleshooting

Profiles Not Loading

  • Check user authentication status
  • Verify backend API connectivity
  • Check browser console for errors

Publishing Failures

  • Verify Bitcoin network connectivity
  • Check private key access
  • Ensure sufficient network fees

Sync Issues

  • Check network connectivity
  • Verify sync service status
  • Clear local cache if needed

API Integration

The component integrates with profile management APIs:

// Get user with profiles
GET /api/users/{id}/profiles

// Update profile
PUT /api/profiles/{id}

// Publish profile
POST /api/profiles/{id}/publish

// Create profile
POST /api/profiles

// Switch active profile
POST /api/users/{id}/switch-profile

Notes for Improvement

Enhanced Implementation: The actual component provides more sophisticated features than the basic prompt described:

  • Complete tabbed interface for all profile operations
  • Profile synchronization and discovery capabilities
  • Better error handling and user feedback
  • Support for multiple profile types (Person/Organization)
  • Advanced publishing workflow with transaction tracking
  • Real-time profile updates and conflict resolution