Sample PRO
Let’s go through the code generated by the FireCMS CLI after a PRO project is created. FireCMS is at its core a React library, so the generated code is a React application. The code is structured in a way that you can easily understand and modify it to fit your needs.
Firebase Setup
Section titled “Firebase Setup”The first step involves initializing Firebase using the provided configuration. This is necessary for all Firebase-related operations throughout the app.
You can find the firebaseConfig after creating a new webapp in the Firebase console.
const { firebaseApp, firebaseConfigLoading, configError} = useInitialiseFirebase({ firebaseConfig});This snippet sets up Firebase, checks for loading status, and handles configuration errors.
Data Source and Storage Source
Section titled “Data Source and Storage Source”Your users will need to interact with data and files, so you need to set up data and storage sources. FireCMS provides a Firestore delegate and Firebase storage source for these operations.
const firestoreDelegate = useFirestoreDelegate({ firebaseApp});const storageSource = useFirebaseStorageSource({ firebaseApp});You are free to define your own data source and storage source, but these are the default ones provided by FireCMS. Feel free to reach out to us if you need help setting up your own data source or storage source.
Collection Configuration Plugin
Section titled “Collection Configuration Plugin”The collection editor plugin allows you to include a UI for editing collection configurations. You can choose where
the config is stored, and pass the configuration to the plugin. We include a controller that saves the configuration
in your Firestore database. The default path is __FIRECMS/config/collections.
The controller includes a few methods you can use in your own components to manage the collection configuration.
const collectionConfigController = useFirestoreCollectionsConfigController({ firebaseApp});You are free to define your collections in code, or use the UI to define them. You can also allow the modification in the UI of the collections defined in code. You can then merge the collections defined in code with the ones defined in the UI.
const collectionsBuilder = useCallback(() => { // Here we define a sample collection in code. const collections = [ productsCollection // Your collections here ]; // You can merge collections defined in the collection editor (UI) with your own collections return mergeCollections(collections, collectionConfigController.collections ?? []);}, [collectionConfigController.collections]);In order to add the collection editor plugin, you need to include it in the list of plugins passed to the FireCMS
component.
const collectionEditorPlugin = useCollectionEditorPlugin({ collectionConfigController});Authorization Management
Section titled “Authorization Management”Managing user authentication and permissions is critical for security and proper access control. FireCMS
provides an Authenticator interface that you can implement to define your own authentication logic.
You can validate the user’s access to the main view based on their authentication status and permissions.
const myAuthenticator: Authenticator<FirebaseUserWrapper> = useCallback(async ({ user, authController }) => {
if (user?.email?.includes("flanders")) { // You can throw an error to prevent access throw Error("Stupid Flanders!"); }
const idTokenResult = await user?.firebaseUser?.getIdTokenResult(); const userIsAdmin = idTokenResult?.claims.admin || user?.email?.endsWith("@firecms.co");
console.log("Allowing access to", user);
// we allow access to every user in this case return true; }, []);
const { authLoading, canAccessMainView, notAllowedError } = useValidateAuthenticator({ authController, authenticator: myAuthenticator, dataSourceDelegate: firestoreDelegate, storageSource });(if you use the user management system, you can use the authenticator provided by the UserManagement controller. See below)
User Management
Section titled “User Management”FireCMS PRO includes a user management system that allows you to define roles and permissions for users. The
UserManagement interface provides methods to define roles and permissions, as well as a loading state to manage.
We include a controller that stores user roles and permissions in Firestore. You are free to define your own user
management system.
const userManagement = useBuildUserManagement({ dataSourceDelegate: firestoreDelegate, });then build the user management plugin and include it in the list of plugins passed to the FireCMS component.
const userManagementPlugin = useUserManagementPlugin({ userManagement });You can delegate all the authentication logic to the user management system, by using the authenticator provided by
the UserManagement controller.
const { authLoading, canAccessMainView, notAllowedError } = useValidateAuthenticator({ authController, disabled: userManagement.loading, authenticator: userManagement.authenticator, dataSourceDelegate: firestoreDelegate, storageSource });The Auth Controller
Section titled “The Auth Controller”The AuthController is the controller in charge of managing authentication. It provides methods to sign in, sign out,
and get the current user. You can also define roles for users. You can access this controller from within your components
using the useAuthController hook.
const authController: FirebaseAuthController = useFirebaseAuthController({ firebaseApp, signInOptions: ["google.com", "password"], // you can pick many more options loading: userManagement.loading, defineRolesFor: userManagement.defineRolesFor});In this case we are hooking the AuthController to the UserManagement controller, so we can define roles for users
based on the user management system.
Mode Controller & User Config Persistence
Section titled “Mode Controller & User Config Persistence”Adjusting UI preferences, like theme mode, enhances user experience.
const modeController = useBuildModeController();const userConfigPersistence = useBuildLocalConfigurationPersistence();These controllers enable theme mode toggling and local storage of user preferences.
Navigation Controller
Section titled “Navigation Controller”The internal navigation controller manages the app’s navigation, leveraging the collections and permissions setup.
Here you can define your collections, views, and admin views.
You can also pass the authController and dataSourceDelegate to the NavigationController. Optionally, you can
define the collection permissions in the UserManagement controller.
const collectionsBuilder = useCallback(() => { // Here we define a sample collection in code. const collections = [ productsCollection // Your collections here ]; // You can merge collections defined in the collection editor (UI) with your own collections return mergeCollections(collections, collectionConfigController.collections ?? []); }, [collectionConfigController.collections]);
// Here you define your custom top-level views const views: CMSView[] = useMemo(() => ([{ path: "example", name: "Example CMS view", view: <ExampleCMSView/> }]), []);
const navigationController = useBuildNavigationController({ collections: collectionsBuilder, views, authController, dataSourceDelegate: firestoreDelegate, adminViews: userManagementAdminViews, collectionPermissions: userManagement.collectionPermissions });This controller leverages the built collections and permissions setup to manage navigation efficiently.
Wiring it all up
Section titled “Wiring it all up”Once you have all the controllers set up, you can pass them to the
FireCMS component, along with the plugins you want to use. The FireCMS component will handle the rest, and
render the main view or the login view based on the user’s authentication status.
Note how you can customize the main view based on the user’s authentication status and permissions. The default login view is a Firebase login view, but you can define your own login view.
The SideDialogs component is used to render the lateral dialogs, like the entity detail view.
The NavigationRoutes component is used to render the main navigation routes. It uses react-router-dom to handle
the routing, but you are free to replace it with your own routing system.
return ( <SnackbarProvider> <ModeControllerProvider value={modeController}>
<FireCMS navigationController={navigationController} authController={authController} userConfigPersistence={userConfigPersistence} dataSourceDelegate={firestoreDelegate} storageSource={storageSource} plugins={[dataEnhancementPlugin, importPlugin, exportPlugin, userManagementPlugin, collectionEditorPlugin]} > {({ context, loading }) => {
let component; if (loading || authLoading) { component = <CircularProgressCenter size={"large"}/>; } else { if (!canAccessMainView) { component = ( <FirebaseLoginView allowSkipLogin={false} signInOptions={signInOptions} firebaseApp={firebaseApp} authController={authController} notAllowedError={notAllowedError}/> ); } else { component = ( <Scaffold autoOpenDrawer={false}> <AppBar title={"My amazing CMS"}/> <Drawer/> <NavigationRoutes/> <SideDialogs/> </Scaffold> ); } }
return component; }} </FireCMS> </ModeControllerProvider> </SnackbarProvider> );Find more details about the main components in the Main Components section.