# 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}]"}