-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d3fd05b
commit 46ef649
Showing
27 changed files
with
536 additions
and
464 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Architecture Choices | ||
|
||
This document explains some of the architecture decisions made in the development of Atomic CRM. | ||
|
||
## Views | ||
|
||
Some pages in Atomic CRM require data from multiple tables. To simplify the frontend code and reduce the HTTP overhead, Atomic CRM uses database views to abstract the complexity of the queries. | ||
|
||
For instance, the contact list page displays the number of tasks for each contact. This information is provided by the `contacts_summary` view, defined in the `supabase/migrations/init_db.sql` file. | ||
|
||
When using [the FakeRest data provider](./data-providers.md#setting-up-the-fakerest-data-provider), these views are emulated in the frontend. | ||
|
||
## Triggers | ||
|
||
User credentials are stored in Supabase's `auth.users` table. Supabase does not allow to add columns tp this table. That's why additional user details are stored in a `sales` table created by Atomic CRM. A database trigger is used to automatically sync the `sales` record when a user is created or updated (e.g. for the `first_name` and `last_name` fields). | ||
|
||
The trigger can be found in the `supabase/migrations/init_triggerssql` file. | ||
|
||
## Edge Functions | ||
|
||
Due to the limitations of Supabase, the API does not have a public endpoint to manage users. | ||
|
||
To solve this problem, Atomic CRM uses a `users` edge function in charge of: | ||
|
||
- Verifying the current user's permissions | ||
- Creating and updating users | ||
|
||
Atomic CRM does not support user deletion to avoid data losses. Yet, it is possible to disable a user's account (relying on Supabase's ban feature). | ||
|
||
Atomic also uses Edge functions to handle the webhook and process the received emails. Check the [Inbound Email](./inbound-email.md) documentation for more information. | ||
|
||
The edge functions can be found in the `supabase/functions/` directory. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
# Customizing Atomic CRM | ||
|
||
Developers can customize the Atomic CRM application to suit their business needs. Some of the customizations can be achieved via configuration on the `<CRM>` component, while others require changes to the source code. | ||
|
||
## The `<CRM>` component | ||
|
||
The entry point of the frontend application is the `src/App.tsx` file. By default, this file simply renders the `<CRM>` component, which is the root component of Atomic CRM. | ||
|
||
```tsx | ||
import { CRM } from './root/CRM'; | ||
|
||
const App = () => <CRM />; | ||
|
||
export default App; | ||
``` | ||
|
||
`<CRM>` accepts various props to customize the application domain and look and feel, so the `App.tsx` file is the best place to configure your CRM. | ||
|
||
For instance, the following code snippet shows how to customize the CRM application title, logo, themes, and domain-specific data. | ||
|
||
```tsx | ||
import { CRM } from './root/CRM'; | ||
import { radiantLightTheme, radiantDarkTheme } from 'react-admin'; | ||
|
||
const App = () => ( | ||
<CRM | ||
title="ACME CRM" | ||
logo="./img/logo.png" // The logo path is relative to the public directory | ||
lightTheme={radiantLightTheme} | ||
darkTheme={radiantDarkTheme} | ||
contactGender={[ | ||
{ value: 'male', label: 'He' }, | ||
{ value: 'female', label: 'She' }, | ||
]} | ||
companySectors={['Technology', 'Finance']} | ||
dealCategories={['Copywriting', 'Design']} | ||
dealPipelineStatuses={['won']} | ||
dealStages={[ | ||
{ value: 'opportunity', label: 'Opportunity' }, | ||
{ value: 'proposal-sent', label: 'Proposal Sent' }, | ||
{ value: 'won', label: 'Won' }, | ||
{ value: 'lost', label: 'Lost' }, | ||
]} | ||
noteStatuses={[ | ||
{ value: 'cold', label: 'Cold', color: '#7dbde8' }, | ||
{ value: 'warm', label: 'Warm', color: '#e8cb7d' }, | ||
{ value: 'hot', label: 'Hot', color: '#e88b7d' }, | ||
]} | ||
taskTypes={['Call', 'Email', 'Meeting']} | ||
/> | ||
); | ||
|
||
export default App; | ||
``` | ||
|
||
`<CRM>` accepts the following props: | ||
|
||
| Props | Description | Type | | ||
|-----------------------|-----------------------------------------------------------------------|-----------------| | ||
| contactGender | The gender options for contacts used in the application. | ContactGender[] | | ||
| companySectors | The list of company sectors used in the application. | string[] | | ||
| darkTheme | The theme to use when the application is in dark mode. | RaThemeOptions | | ||
| dealCategories | The categories of deals used in the application. | string[] | | ||
| dealPipelineStatuses | The statuses of deals in the pipeline used in the application | string[] | | ||
| dealStages | The stages of deals used in the application. | DealStage[] | | ||
| lightTheme | The theme to use when the application is in light mode. | RaThemeOptions | | ||
| logo | The logo used in the CRM application. | string | | ||
| noteStatuses | The statuses of notes used in the application. | NoteStatus[] | | ||
| taskTypes | The types of tasks used in the application. | string[] | | ||
| title | The title of the CRM application. | string | | ||
|
||
## Customizing The Theme | ||
|
||
Atomic CRM uses the Material-UI library for theming. You can customize the light and dark themes by setting the `lightTheme` and `darkTheme` props on the `<CRM>` component. | ||
|
||
Check out react-admin's [theming documentation](https://marmelab.com/react-admin/Theming.html) for more information on how to customize the themes. | ||
|
||
## Customizing The Layout | ||
|
||
The components that make up the layout of the application (menu, container, etc) are located in the `src/layout` directory. You can customize the layout by modifying these components. | ||
|
||
## Customizing the Homepage | ||
|
||
The home page of the application is rendered by the `Dashboard.tsx` component. Updating this file to customize the dashboard. | ||
|
||
Here is a simple example of a customized dashboard: | ||
|
||
```jsx | ||
// ./src/dashboard/Dashboard.tsx | ||
import React from 'react'; | ||
import { Card, CardContent, Typography } from '@mui/material'; | ||
|
||
export const Dashboard = () => ( | ||
<Card> | ||
<CardContent> | ||
<Typography variant="h5" component="div"> | ||
Welcome to the Custom Dashboard! | ||
</Typography> | ||
<Typography variant="body2" color="text.secondary"> | ||
This is a customized homepage for your application. You can add any components or content here to suit your needs. | ||
</Typography> | ||
</CardContent> | ||
</Card> | ||
); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Data Providers | ||
|
||
By default, the CRM demo uses [Supabase](https://supabase.com) for the backend API. Supabase is an open-source alternative to Firebase, built on top of Postgres. It provides a REST API and a real-time subscription system. The generous free tier allows you to run a small CRM for free. | ||
|
||
## Using A Fake API For Development | ||
|
||
For development purposes, you can use an alternative data provider called [FakeRest](https://github.com/marmelab/FakeRest). It's a simple REST API running in the browser that resets the data on each page reload. It's useful for testing the frontend without having to set up a backend, e.g. to let end users test some updates before the backend is ready. | ||
|
||
FakeRest is used in the [React Admin CRM demo](https://marmelab.com/react-admin-crm/), where you can test it live. | ||
|
||
### Setting Up The FakeRest Data Provider | ||
|
||
To set up the FakeRest data provider, you need to change the `dataProvider` import in the `src/App.tsx` file: | ||
|
||
```diff | ||
// in src/App.tsx | ||
import { CRM } from './root/CRM'; | ||
+ import { dataProvider, authProvider } from './providers/fakerest'; | ||
|
||
const App = () => ( | ||
<CRM | ||
+ dataProvider={dataProvider} | ||
+ authProvider={authProvider} | ||
/> | ||
); | ||
|
||
export default App; | ||
``` | ||
|
||
### Filters Syntax | ||
|
||
The list filters used in this project MUST follow the [`ra-data-postgrest`](https://github.com/raphiniert-com/ra-data-postgrest) convention, where the filter operator is concatenated to the field name with an `@`. For example, to filter contacts by first name, you would use the `first_name@eq` filter. | ||
|
||
When using FakeRest, the filters are mapped at runtime to the FakeRest filter syntax by the the [`supabaseAdapter`](../../src/providers/fakerest/internal/supabaseAdapter.ts) file. If a filter is not yet supported by the adapter, you have to modify this file to add support for it. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# Deploying to Production Manually | ||
|
||
## Deploying The Backend | ||
|
||
The entire backend of Atomic CRM is hosted on Supabase. The backend is composed of a Postgres database, a REST API, and edge functions. Check out the [Supabase Configuration](./supabase-configuration.md) section for details. | ||
|
||
After configuring your Supabase instance, you can deploy the backend changes with the following command: | ||
|
||
```sh | ||
make supabase-deploy | ||
``` | ||
|
||
## Testing Production Mode | ||
|
||
If you want to test you local frontend code using the remote Supabase instance and the production settings, you can run the following command: | ||
|
||
```sh | ||
make prod-start | ||
``` | ||
|
||
Note: It will apply migrations and deploy edge functions. | ||
|
||
You can then access the app via [`http://localhost:3000/`](http://localhost:3000/). | ||
|
||
## Deploying The Frontend | ||
|
||
The frontend of the CRM is a Single-Page App that can be deployed to any CDN, or to GitHub Pages. | ||
|
||
First, build the fontend bundle with: | ||
|
||
```sh | ||
make build | ||
``` | ||
|
||
This will create a `dist` directory with the built application made of static HTML, CSS, and JS files. Upload this directory to the CDN of your choice. | ||
|
||
If you want to deploy it to GitHub pages, you can use the following command: | ||
|
||
```sh | ||
npm run ghpages:deploy | ||
``` | ||
|
||
The CRM will be available at `https://<username>.github.io/atomic-crm/`. | ||
|
||
## Deploying Updates | ||
|
||
If you've modified the code, run the following command to deploy a new version of your CRM: | ||
|
||
```sh | ||
make prod-deploy | ||
``` | ||
|
||
It will apply migrations, deploy edge functions and push the built applications to the `gh-pages` branch. | ||
|
||
## Automating Deployments With GitHub Actions | ||
|
||
Atomic CRM contains GitHub actions for continuous integration and delivery. To enable these actions, you will | ||
have to create the following secrets on GitHub: | ||
|
||
```bash | ||
SUPABASE_ACCESS_TOKEN: Your personal access token, can be found at https://supabase.com/dashboard/account/tokens | ||
SUPABASE_DB_PASSWORD: Your supabase database password | ||
SUPABASE_PROJECT_ID: Your supabase project id | ||
SUPABASE_URL: Your supabase project URL | ||
SUPABASE_ANON_KEY: Your supabase project anonymous key | ||
POSTMARK_WEBHOOK_USER: User configured in Postmark to secure the webhook | ||
POSTMARK_WEBHOOK_PASSWORD: Password configured in Postmark to secure the webhook | ||
POSTMARK_WEBHOOK_AUTHORIZED_IPS: List of IPs (comma separated) authorized to send requests to the Postmark webhook | ||
``` | ||
|
||
> **Note:** The `POSTMARK_*` variables are required for Atomic CRM's inbound email features. Have a look at the the [inbound email configuration](./inbound-email-configuration.md) to learn more about their usage and setup. | ||
The GitHub action will run the `prod-deploy` command on every push to the `main` branch, deplyiong the frontend to GitHub pages and updating the Supabase instance. |
Oops, something went wrong.