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:
- 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. - 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:
-
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 theregisterUser
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
-
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 theregisterUserWithGoogleProvider
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.