Organizations

Organizations

The Organizations feature in ShipFast allows users to manage and collaborate within their organizations. A user is automatically assigned to a default organization upon registration, and they are the owner of that organization. Users can invite members to their organization, and the platform supports both registered and unregistered users. Members who are already registered receive an in-app notification, while unregistered members receive an email invitation.

Key Features:

  • Default Organization Creation: Upon registration, users are assigned to their default organization.
  • User Ownership: The first user in an organization is assigned as the owner.
  • Invitation System: Users can invite both registered and unregistered members to join their organization.
  • Email Notifications: Registered users receive email notifications, while non-registered users receive email invitations.
  • Multiple Organizations: A user can belong to multiple organizations, allowing for flexibility in team and project management.

How It Works

Default Organization Creation

When a user registers with email and password or via Google, ShipFast automatically creates a default organization for the user. The name of the organization is based on the user's email address or display name, and the user is set as the owner of the organization. The user can then invite others to join this organization.

Invitation System

Users can invite others to their organization by providing their email addresses. The system handles two scenarios:

  1. Registered Users: If the invitee is already registered with ShipFast, identified by their userId, they will receive an email notification and can easily join the organization.
  2. Unregistered Users: If the invitee is not registered (no userId), they will receive an email invitation with a link to complete the registration process and join the organization.

The sending of these emails is managed by a Firestore Trigger, which listens for changes to the user_organizations collection. Specifically, when a document in this collection is modified and the isInviteAccepted or inviteUrl properties are updated, the trigger will automatically send an email notification or invitation based on whether the invitee is already registered (has a userId).

For a more detailed explanation of how this Firestore trigger works, you can refer to the documentation here: Notification Emails.

Membership Management

Once users are invited, they can be assigned one of the following roles within the organization: Owner, Admin, or Member.

  • The Owner has full control over the organization and its settings, including the ability to assign roles and manage permissions.
  • The Admin can manage users and settings but does not have full control over critical organization settings reserved for the Owner.
  • The Member has limited access, typically focused on participating in the organization's activities without administrative privileges.

Implementation Details

User Registration and Organization Creation

When a user registers on ShipFast, a default organization is automatically created for them, and they are set as the owner of that organization. There are multiple ways a user can register or log in:

  1. Email and Password Registration:

    • On the login page, a user can sign up by entering their email address and password.
    • The system will then create a default organization for the user using their email or display name as the organization’s name, and the user will automatically become the owner of that organization.

    The main function responsible for this process is the registerUser function, which handles the creation of both the user account and the default organization. Here’s the code for the registerUser function:

    export const registerUser = (email, password) => {
      return new Promise(async (resolve, reject) => {
        try {
          // Create Firebase Auth account with email and password
          const { user } = await createUserWithEmailAndPassword(
            auth,
            email,
            password
          );
     
          if (!user) {
            return reject(new Error("User creation failed"));
          }
     
          const orgName = `${user.email || user.displayName} Org`; // Default organization name
     
          // Create the organization and add user
          const [organization] = await Promise.all([
            createOrganizationAndAttachUser(user.uid, {
              name: orgName,
              billingEmail: user.email || ""
            }),
            addUser({
              userId: user.uid,
              email: user.email || "",
              name: user.displayName || ""
            }),
            sendEmailVerification(user)
          ]);
     
          const orgId = organization?.id || organization?.organizationId;
          const metadata = {
            organizationId: orgId,
            userId: user.uid,
            email: user.email
          };
     
          // Create a free Stripe subscription for the user
          await createStripeFreeSubscription(user.email, orgId, metadata);
     
          resolve(); // Registration successful
        } catch (error) {
          reject(new Error(`Registration failed: ${error.message}`)); // Error handling
        }
      });
    };

    File Path: src/services/user.js

  2. Google Sign-Up and Sign-In:

    For users signing up or logging in via Google, ShipFast will handle the registration and organization creation automatically. If the user is signing in for the first time, their default organization will be created, and they will be assigned as the owner.

    The main function responsible for this process is the registerUserWithGoogleProvider function, which handles the user authentication and creation of both the user account and the default organization. Here’s the code for the registerUserWithGoogleProvider function:

    export const registerUserWithGoogleProvider = () => {
      return new Promise(async (resolve, reject) => {
        try {
          // Register user in Firebase Auth from Google Popup
          const { user } = await signInWithPopup(auth, googleProvider);
          if (!user) {
            return reject(new Error("User registration failed."));
          }
     
          const userId = user.uid;
          const existingUser = await getUserById(userId);
     
          // If the user already exists, resolve immediately
          if (existingUser) {
            return resolve({
              message: "User already exists.",
              user: existingUser
            });
          }
     
          const orgName = `${user.email || user.displayName} Org`;
     
          const [organization] = await Promise.all([
            // Create an organization in the database linked to the user
            createOrganizationAndAttachUser(user.uid, {
              name: orgName,
              billingEmail: user.email || ""
            }),
            // Create a user record in the database
            addUser({
              userId,
              email: user.email || "",
              name: user.displayName || ""
            })
          ]);
     
          const orgId = organization?.id || organization?.organizationId; // Retrieve the organization ID
          const metadata = {
            organizationId: orgId,
            userId: user.uid,
            email: user.email
          };
     
          // Register the user for a free plan in Stripe
          await createStripeFreeSubscription(user.email, orgId, metadata);
     
          // Resolve the Promise if everything succeeds
          resolve({ user, organization });
        } catch (error) {
          // Reject the Promise with the error
          reject(error);
        }
      });
    };

File Path: src/services/user.js

This function ensures that when a user signs up via Google, they automatically receive a default organization and are set up for the free plan in Stripe. If the user already exists, it resolves with the existing user without creating a new account or organization.