# Site Structure ### Goals 1. Make the site more maintainable 3. Make it so that circular dependancies don't happen anymore 4. Make features have a clear separation of concerns (and components) - decrease tight coupling 5. Allow features to have public pages 6. Don't make cross-feature components ``` src features events hooks eventHooks engagement pages Engagements Engagment EngagementDetails EngagementSessions EngagementSession EngagementTasks EngagementMessages EngagementAdvanced EngagementAdvancedNotifications EngagementWizard EngagementWizardDetails modals PartnerDrawer AccountDrawer SessionModal EngagementModal components PartnerTag SessionStatusMenu EngagementsList MessagesList types engagement engagementDetail stores someContext hooks engagementHooks routes.ts feature.ts shared components hooks types ``` **!! Moving these hooks to their feature conflicts with select fromSource !!** I'm not sure that it's a good idea to import items from a feature to what was supposed to be a more generic form component ### Routes What if we tried nested routes for children? ```javascript= const engagementRoutes = { engagements: { exact: true, path: '/:programUid/events', component: React.lazy(() => import('./pages/Engagements')) }, engagementWizard: { path: '/:programUid/events/wizard/:engagementId', component: React.lazy(() => import('./pages/EngagementWizard')) routes: { details: { path: '/details', component: React.lazy(() => import('./pages/EngagementWizardDetails')), }, sections: { path: '/sections', component: React.lazy(() => import('./pages/EngagementWizardSections')), }, accounts: { path: '/accounts', component: React.lazy(() => import('./pages/EngagementWizardAccounts')), }, partners: { path: '/partners', component: React.lazy(() => import('./pages/EngagementWizardPartners')), }, sessions: { path: '/sessions', component: React.lazy(() => import('./pages/EngagementWizardSessions')), }, tasks: { path: '/tasks', component: React.lazy(() => import('./pages/EngagementWizardTasks')), }, publish: { path: '/publish', component: React.lazy(() => import('./pages/EngagementWizardPublish')), },, } }, engagement: { path: '/:programUid/events/:engagementId', component: React.lazy(() => import('./pages/Engagement')), routes: { details: { path: '/details', component: React.lazy(() => import('./pages/EngagementDetails')), } sessions: { path: '/sessions', component: React.lazy(() => import('./pages/EngagementSessions')), routes: { session: { path: /:sessionId, component: React.lazy(() => import('./pages/EngagementSession')), } } }, tasks: { path: '/tasks', component: React.lazy(() => import('./pages/EngagementTasks')), } messages: { path: '/messages', component: React.lazy(() => import('./pages/EngagementMessages')), } } }, modal: { session: { path: '/:programUid/events/:engagementId/sessions/:sessionId/edit' component: React.lazy(() => import('./modals/SessionModal')), }, engagment: { path: '/:programUid/events/:engagementId/edit' component: React.lazy(() => import('./modals/EngagementModal')), } }, drawer: { partner: { path: '/:programUid/events/:engagementId/partners' component: React.lazy(() => import('./modals/PartnerDrawer')), }, account: { path: '/:programUid/events/:engagementId/accounts' component: React.lazy(() => import('./modals/AccountDrawer')), }, } // extended example network: { eventsSummary: { path: '/network/events-summary', component: React.lazy(() => import('./network/EventsSummary')), } }, settings: { ??? } /* * We have different settings that we need to manage * user settings * program settings * organization settings * * what else?... * */ } ``` ### Feature The feature config can include dashboard modules. We can use these modules to programatically add/remove modules to a program's dashboard. ``` const engagmentFeature = { routes, menuItems dashboardModules: { engagements: React.lazy(() => import('./components/EngagementsList')) messages: React.lazy(() => import('./components/MessagesList')) } config initializer } ``` *Example of a program utilizing these features and their dashboard modules* ``` const cosellProgram = { settings: { features: { engagementFeature, heatmapFeature } dashboard: { announcements: true programStats: true, jumpLinks: true modules: [ { component: heatmap, index: 0 }, { component: engagements, index: 1 }, { component: messages, index: 2 } ] } } } ``` *Example of a reusable component that could be used in a dashboard module as well as an Engagement page* ```jsx= function EngagementsList({ query }) { const defaultQuery = query ?? { pageSize: 5, sort: 'created-' } const { data } = engagementHooks.useGetAll() return <List dataSource={data} ... /> } ``` Features should also be able to declare public pages accessible outside of the authenticated layout. ``` type RouteProps = { ...props, requireAuth?: boolean } ``` ### Global Feature??? What do we do with modals that are basically the same between features? - ex: TaskModal - Handles the editing of tasks - Some of these modals are using slightly different params **TODO** Current thoughts: if a modal has specific params then it should be a separate component ### Multi-Feature Walkthrough What happens when something we build is dependent on two or more features? Feature walkthroughs can be defined at the feature level. Multi-level walkthroughs could be defined at the program level. In the case of DTA, they have a walkthrough that uses 3 features. Unless another program uses those 3 same features, they won't ever need that walkthrough. ### Program Context Don't base the program context off of the uid in the url. 1. A program is initialized when the user navigates to a url that contains that programs uid or the settings page asks for a certain programs data - create a hook that can access/initialize a program based on the id/uid it receives 2. Upon initialization, memoize the program data and only re-initalize if the user changes - (create a developer only view for viewing a programs initialized data in a json format) 3. Easily access the program's initialized data using a hook that accepts the program id or program uid ``` RootContext ProgramContext App pages /login /welcome /customPage /customPage2 ... network settings programs ``` ### Sharing components between features So far, I've mostly seen this between Engagements and Events. This can be attributed to the fact that they are the same entity in the database. While these are 'shared' components, I don't think they belong in shared/components. They are tightly coupled to the features they are used in. ``` ``` ### Higher order components Are there places in our code where we could benefit from using higher order components? It seems that hooks mostly take care of this. ### Code Splitting with Feature Config In our feature config, we could include a list of modules that can be included on a certain page. ``` // TODO ``` ### Bulletproof-React ``` src features heatmap pages modals components types stores hooks heatmapHooks heatmapAccount routes.ts feature.ts massCosell pages modals components types stores hooks massCosellRequestHooks massCosellSessionHooks heatmapAccount routes.ts feature.ts components hooks types utils ``` In this format, features could list dependencies. - Ex. massCosell has a dependency on heatmaps ``` src features cosell subFeatures engagements pages modals dashboardModules feature.ts routes.ts engagments2 (client engagements) heatmaps ... massCosell ... hooks heatmapAccounts types context events hooks types context partners ... reports ... ``` #### Json Form In its current form, this will be affected by how we structure our features The following is an example of a cross-feature query that would be allowed by our current setup ```typescript= const elements = [ { type: 'select', name: 'accounts', fromSource: 'accounts' } ] ``` *Proposal:* When creating features, include an area in each feature where we can add sources that can be queried using our json form. File Structure ``` src features cosell config.ts ``` Config.ts ```typescript= const cosellSources = [ { source: 'eventPartner', useHook: eventPartnerHooks.useGetAll, query: { micro: true }, }, { source: 'accounts', useHook: heatmapAccountHooks.useGetAll, query: { micro: true }, } ] ``` ### General Components 1. Should just be presentational components - don't care about context user, permissions, api requests, enums, etc...
{"metaMigratedAt":"2023-06-16T05:13:45.028Z","metaMigratedFrom":"Content","title":"Site Structure","breaks":true,"contributors":"[{\"id\":\"13458f76-1a0c-445a-8b63-dfc60f5438fc\",\"add\":10904,\"del\":960}]"}
Expand menu