A single page app for managing triniti schemas and services.
See the demo README.
The codebase is organized into two main packages:
The core CMS package that contains all the reusable components, utilities, and functionality. This package includes:
- Higher-Order Components (HOCs)
- React Hooks
- Utility functions
- Shared components
- Core business logic
All imports from this package should use the @triniti/cms prefix, for example:
import{withPbj}from'@triniti/cms/components/with-pbj';import{formatDate}from'@triniti/cms/utils/date';The application-specific package that implements the CMS for a specific vendor. This package includes:
- Vendor-specific components
- Custom implementations
- Configuration
- Routes
- Vendor-specific business logic
All imports from this package should use the @triniti/app prefix, for example:
import{MyCustomComponent}from'@triniti/app/components/my-custom-component';import{vendorConfig}from'@triniti/app/config';withPbj(Component,curie,initialData={})Wraps a component with PBJ (Protocol Buffer JSON) functionality. This HOC handles loading states, provides PBJ data to the wrapped component, and uses the useResolver hook internally for data resolution.
Location:@triniti/cms/components/with-pbj/index.js
Parameters:
Component- The React component to wrapcurie- The PBJ curie identifierinitialData- Optional initial data object (defaults to empty object)
Returns: A new component that provides PBJ data through props
Example:
constMyComponent=({ pbj })=>(<div>{pbj.get('title')}</div>);exportdefaultwithPbj(MyComponent,'acme:article:node:1');withForm(Component,config={})Wraps a component with comprehensive form handling capabilities. This HOC provides form state management, initialization, and restoration capabilities. It integrates with Redux for state management and includes built-in error handling and loading states.
Location:@triniti/cms/components/with-form/index.js
Parameters:
Component- The React component to wrapconfig- Configuration object with options:restorable- Boolean to enable form state restorationkeepDirtyOnReinitialize- Boolean to maintain dirty state on reinitialization
Example:
constArticleForm=({ form, handleSubmit, formState })=>(<formonSubmit={handleSubmit}><input{...form.getFieldProps('title')}placeholder="Article Title"/><buttontype="submit">Save</button></form>);exportdefaultwithForm(ArticleForm,{restorable: true,keepDirtyOnReinitialize: true});withBlockModal(Component)Wraps a component with block modal functionality, providing a standardized interface for editing and managing content blocks within a modal context.
Location:@triniti/cms/blocksmith/components/with-block-modal/index.js
Example:
constBlockEditor=({ block, onSave })=>(<div><h2>Edit Block</h2>{/* Block editing UI */}</div>);exportdefaultwithBlockModal(BlockEditor);withBlockPreview(Component)Wraps a component with block preview functionality, enabling real-time preview of content blocks with proper formatting and styling.
Location:@triniti/cms/blocksmith/components/with-block-preview/index.js
Example:
constBlockDisplay=({ block })=>(<divclassName="block-preview"><h3>{block.get('title')}</h3><div>{block.get('content')}</div></div>);exportdefaultwithBlockPreview(BlockDisplay);withNodeScreen(Screen,config)Wraps a screen component with node-related functionality, providing a standardized interface for viewing and managing node data with built-in CRUD operations.
Location:@triniti/cms/plugins/ncr/components/with-node-screen/index.js
Example:
constArticleScreen=({ node, onUpdate })=>(<div><h1>{node.get('title')}</h1><div>{node.get('content')}</div><buttononClick={()=>onUpdate(node)}>Update</button></div>);exportdefaultwithNodeScreen(ArticleScreen,{nodeType: 'article'});withRequest(Component,curie,config)Wraps a component with request handling capabilities, providing a standardized way to make and manage API requests with built-in loading and error states. This HOC provides the request object that can be used with the useRequest hook for actual request handling.
Parameters:
Component- The React component to wrapcurie- The PBJ curie identifier for the requestconfig- Configuration object for request initialization
Returns: A new component that provides:
request- The PBJX request object to be used withuseRequestpbj- The same request object (for backward compatibility)
Location:@triniti/cms/plugins/pbjx/components/with-request/index.js
Example:
constSearchResults=({ request })=>{const{ response, pbjxError, run, isRunning }=useRequest(request);if(isRunning)return<Loading/>;if(pbjxError)return<div>Error: {pbjxError}</div>;return(<div>{response.get('nodes').map(node=>(<divkey={node.get('_id')}>{node.get('title')}</div>))}<buttononClick={run}>Refresh</button></div>);};exportdefaultwithRequest(SearchResults,'acme:search:request:1',{channel: 'search',initialData: {count: 10,sort: 'created_at_desc'}});useResolver(curie,initialData)Hook for resolving PBJ data, providing a simple way to fetch and manage PBJ data with built-in loading states and error handling.
Parameters:
curie- The PBJ curie identifierinitialData- Optional initial data
Location:@triniti/cms/components/with-pbj/useResolver.js
Used by:withPbj HOC
Example:
constMyComponent=()=>{constpbj=useResolver('acme:article:node:1',{title: 'Default Title'});if(!pbj)return<Loading/>;return<div>{pbj.get('title')}</div>;};useRequest(request,runImmediately=true,enricher=noop)Hook for making and managing PBJX requests, providing a standardized way to handle API requests with built-in loading states, error handling, and request lifecycle management.
Parameters:
request- The PBJX request object to executerunImmediately- Boolean to determine if the request should run on mount (defaults to true)enricher- Optional function to enrich the request before sending (defaults to noop)
Returns:
response- The PBJX response objectrun- Function to manually trigger the requestisRunning- Boolean indicating if the request is in progresspbjxError- Any error that occurred during the requestpbjxStatus- Current status of the request (none, pending, fulfilled, failed)
Location:@triniti/cms/plugins/pbjx/components/useRequest.js
Example:
constSearchComponent=()=>{constrequest=SearchRequest.create().set('q','search term').set('count',10);const{ response, run, isRunning, pbjxError }=useRequest(request);if(isRunning)return<Loading/>;if(pbjxError)return<div>Error: {pbjxError}</div>;return(<div>{response.get('nodes').map(node=>(<divkey={node.get('_id')}>{node.get('title')}</div>))}<buttononClick={run}>Refresh</button></div>);};getExt(fileName)Extracts the file extension from a filename.
Location:@triniti/cms/utils/file.js
Parameters:
fileName- A file name including extension
Returns: The file extension or undefined if none exists
Example:
getExt('document.pdf')// returns 'pdf'getExt('image.jpg')// returns 'jpg'removeExt(fileName)Removes the extension from a filename.
Location:@triniti/cms/utils/file.js
Parameters:
fileName- A file name including extension
Returns: The filename without extension
Example:
removeExt('document.pdf')// returns 'document'insertBeforeExt(fileName,str)Inserts a string before the file extension.
Location:@triniti/cms/utils/file.js
Parameters:
fileName- A file name including extensionstr- String to insert before the extension
Returns: Modified filename with string inserted before extension
Example:
insertBeforeExt('image.jpg','-thumb')// returns 'image-thumb.jpg'formatDate(date,format='MMM dd, yyyy hh:mm a',dateOnly=false)Formats a date using date-fns.
Location:@triniti/cms/utils/date.js
Parameters:
date- Date object or string to formatformat- Format string (defaults to 'MMM dd, yyyy hh:mm a')dateOnly- Boolean to format only the date portion
Returns: Formatted date string
Example:
formatDate(newDate())// returns 'Jan 01, 2024 12:00 PM'formatDate(newDate(),'yyyy-MM-dd',true)// returns '2024-01-01'formatBytes(bytes)Converts bytes to a human-readable format.
Location:@triniti/cms/utils/format.js
Parameters:
bytes- Number of bytes
Returns: Formatted string (e.g., '1.5 MB')
Example:
formatBytes(1500000)// returns '1 MB'formatBytes(500)// returns '500 bytes'createReducer(initialState,handlers)Creates a Redux reducer with automatic action type handling.
Location:@triniti/cms/utils/redux.js
Parameters:
initialState- Initial state for the reducerhandlers- Object mapping action types to handler functions
Returns: Reducer function
Example:
constreducer=createReducer({count: 0},{INCREMENT: (state)=>({ ...state,count: state.count+1}),DECREMENT: (state)=>({ ...state,count: state.count-1})});progressIndicator.show(title)progressIndicator.update(title)progressIndicator.close()Manages loading indicators using SweetAlert2.
Location:@triniti/cms/utils/progress.js
Example:
progressIndicator.show('Loading...');// do somethingprogressIndicator.close();damUrl(id)Generates a DAM (Digital Asset Management) URL for an asset.
Location:@triniti/cms/utils/url.js
Parameters:
id- Asset ID
Returns: Complete DAM URL
Example:
damUrl('image-123')// returns 'https://dam.acme.com/image-123'nodeUrl(nodeRef)Generates a URL for a node.
Location:@triniti/cms/utils/url.js
Parameters:
nodeRef- Node reference
Returns: Complete node URL
Example:
nodeUrl('acme:article:node:123')// returns '/article/123'The codebase follows a pattern of using Higher-Order Components (HOCs) for cross-cutting concerns and React hooks for state management and data fetching. The with* functions are primarily used for component composition and providing additional functionality, while the use* hooks are used for state management and data fetching.
Each of these functions serves a specific purpose in the application's architecture:
- Form handling (
withForm) - PBJ data management (
withPbj,useResolver) - UI components (
withBlockModal,withBlockPreview) - Feature-specific functionality (
withNodeScreen,withRequest)