@bestdoctor/ke
v4.26.0-beta.4
Published
BestDoctor back-office UI constructor
Downloads
421
Readme
ke
React admin framework done by and for backenders
ALPHA VERSION – NOT FOR PRODUCTION USE
Installation
$ yarn add @bestdoctor/ke
Usage example
This is an example of the future API
To use ke
you should implement several base classes.
First of all you should extend BaseProvider
class.
It implements the basic methods for HTTP interaction using an axios instance.
Thus, you should have an axios instance with the required headers and pass
it to the provider class.
// client.ts
import axios from 'axios'
const httpClient = axios.create({
baseURL: process.env.API_URL,
})
export { httpClient }
// provider.ts
import { BaseProvider } from '@bestdoctor/ke'
import { httpClient } from 'client'
class Provider extends BaseProvider {
constructor() {
super(httpClient)
}
}
export { Provider }
After that you should extend BaseAdmin class. Here you can declaratively describe your component.
In admin class you can describe:
- baseUrl: string – resource for API interaction
getResource(lookupField?: string | undefined): string
- method to get resource url string for backend requests.- Fields for list view
- Fields for detail view
- Filters for list view
- Template filters for list view
As a result, your admin class will look like this:
// admin.tsx
import {
BaseAdmin,
BaseFilter,
LinkWidget,
ReadOnlyWidget,
SelectWidget,
ForeignKeySelectWidget,
} from '@bestdoctor/ke';
import { PatientProvider } from './providers';
class PatientAdmin extends BaseAdmin {
list_filters = {
name: 'id',
label: 'ID',
Filter: BaseFilter,
}
list_fields = [
{
id: 'id',
Header: 'ID',
toDetailRoute: '/patients',
accessor: (row: any) => row.id,
},
]
detail_fields = [
{
name: 'full_name',
widget: LinkWidget,
helpText: 'Full name',
href: (object: any) => object.admin.url,
layout: { x: 2, y: 1, w: 2, h: 1, static: true },
},
{
name: 'user.email',
widget: ReadOnlyWidget,
helpText: 'User email',
layout: {x: 0, y: 0, w: 1, h: 2, static: true},
},
{
name: 'status',
widget: SelectWidget,
helpText: 'Status',
dataSource: `${process.env.API_URL}appeal_results/`,
layout: {x: 3, y: 3, w: 1, h: 2, static: true},
},
{
name: 'user',
widget: ForeignKeySelectWidget,
helpText: 'User select',
dataSource: `${process.env.API_URL}users/`,
targetPayload: (object: any) => ({ user: object.email }),
optionLabel: (object: any) => object.full_name,
optionValue: (object: any) => object.email,
layout: { x: 9, y: 13.5, w: 2, h: 1, static: true },
}
]
getResource(lookupField?: string | undefined): string {
const baseUrl = `${process.env.API_URL}patients/`
return getAdminResource(baseUrl, lookupField)
}
}
To get more info about fields description, check here
After that you can use ResourceComposer
, AdminResource
and Resource
components,
which makes all magic under the hood and get your user component.
AdminResource
component takes the following arguments:
admin
– instance of your admin classprovider
– instance of your provider implementationuser
– user object from your backend. Used for permissions and analytics systemanalytcs
– analytics object instance. See below for more details.
import { ResourceComposer } from '@bestdoctor/ke';
import { ChakraProvider } from '@chakra-ui/react';
import { Provider } from 'provider';
const provider = new Provider()
const App = () => (
<ChakraProvider>
<ResourceComposer>
<AdminResource
name="patients"
admin={new PatientAdmin()}
provider={provider}
user={{ name: 'name', permissions: ['admin_user'] }}
analytics={analyticsInstnace}
/>
</ResourceComposer>
</ChakraProvider>
)
App
will have routes for the list and detail view with the
settings and widgets you set.
Resource
component can be used in case you want to mount custom widget to
ke
routing and use it features under the hood. For example, login page:
<Resource name="login">
<Login />
</Resource>
If you want ke to generate a side bar with all sections of your spa, use
withSideBar
setting:
<ResourceComposer withSideBar>
...
</ResourceComposer>
Permissions
You can restrict access to different sections of your system, as well as to individual widgets.
To restrict access to an entire section, define permissions list in your admin class:
import { BaseAdmin } from '@bestdoctor/ke'
class MyAdmin extends BaseAdmin {
baseUrl = 'https://test.com'
permissions = ['my_admin_access_permission']
list_fields = ...
}
and pass current user permissions list to ResourceComposer
element:
<ResourceComposer permissions={currentUser.permissions}>
...
</ResourceComposer>
To render a specific widget based on user permissions, use hasPermission
util in detail fields settings:
import { hasPermission } from '@bestdoctor/ke'
...
{
name: 'status.text',
helpText: 'Status',
widget: (user: { permissions: string[] }) => (
hasPermission(user.permissions, 'admin_permission') ? SelectWidget : ReadOnlyWidget
),
displayValue: (object: any) => object.status,
targetPayload: (value: any) => ({ status: value }),
layout: { x: 9, y: 9, w: 2, h: 1, static: true },
},
Monitoring
Out of the box we support error monitoring via Sentry
To include it in your application, use the component EnableSentry
at the root of your application.
// App.tsx
import { EnableSentry } from '@bestdoctor/ke'
const App = (): JSX.Element => (
<>
<EnableSentry sentryDSN={sentryDSN} />
<h1>Hello, world</h1>
</>
)
For application perfomance monitoring we support ELK APM You can use it via EnableELK component
import { EnableELK } from '@bestdoctor/ke'
const App = (): JSX.Element => (
<>
<EnableELK
serviceName={ELK_SERVICE_NAME}
serverUrl={ELK_SERVER_URL}
serviceVersion={ELK_SERVICE_VERSION}
/>
<h1>Hello, world</h1>
</>
)
Analytics
ke
has default integration with google analytics through firebase.
You can use it or connect the service you need. To use build-in analytics
create instance:
import { FirebaseAnalytics } from '@bestdoctor/ke'
const firebaseConfig = {
apiKey: 'secret',
authDomain: 'auth.domain',
databaseURL: 'database.url',
projectId: 'projectID',
storageBucket: '',
messagingSenderId: '',
appId: '',
measurementId: '',
userId: '',
}
const analytics = new FirebaseAnalytics(firebaseConfig)
and pass it to your AdminResource
:
<AdminResource
name="test"
admin={new TestAdmin()}
provider={provider}
user={currentUser}
analytics={analytics}
/>
If you want to use another analytics service, you must define on your side a client that will implement the base interface, by analogy with firebase
Analytics hanlder here is object, that will handle events submit (in case you use SDK).
ke
has default analytics payload format:
- eventName: string – name of event. By default defined here
- widgetName: string – name of widget that triggered event
- widgetType: string – type of widget that triggered event. By default defined here
- value: string | object – value to send with event
- viewType: string – current view type (list or detail)
- resourceName: string – name of the backend resource on which the event occurred
- resourceId: string – id of the backend resource on which the event occurred (in case of detail view)
- url: string – current location in SPA routing
You can override it with widgetAnalytics
setting
in your admin class settings
(place where you describe your widgets layout).
This setting can be declared as an arrow function that takes all of the above parameters. The return value of this function will be used as the body of the request when sent to the analytics:
{
name: 'test_widget',
widget: SelectWidget,
widgetAnalytics: ({ eventName, viewType, value, widgetName }) => (
{ customEventKey: { eventName, ... }}
),
...
}
To disable analytics for a specific widget, you can declare a widgetAnalytics
setting as false
:
{
name: 'test_widget',
widget: SelectWidget,
widgetAnalytics: false,
...
}
If you want to use analytics in your custom component-based widget,
you can use pushAnalytics
handler
It encapsulates the logic for unpacking and sending the event to the analytics.
Among other arguments described above, it takes widgetAnalytics
parameter.
pushAnalytics({
eventName: EventNameEnum.BUTTON_CLICK,
widgetType: WidgetTypeEnum.ACTION,
value: 'some value',
widgetAnalytics: true,
widgetName,
viewType: 'some_action',
resource: 'some_resource',
...props,
})
props
here is the properties,
that ke
injects in your custom widget when renders them from admin settings.
import type { WidgetProps } from '@bestdoctor/ke'
const CustomWidget = (props: WidgetProps): JSX.Element => {
const handleUserAction = (): void => {
pushAnalytics({
eventName: EventNameEnum.BUTTON_CLICK,
widgetType: WidgetTypeEnum.ACTION,
value: 'some value',
widgetAnalytics: true,
widgetName,
viewType: 'some_action',
resource: 'some_resource',
...props,
})
...
}
return (
<Button onClick={handleUserAction}>TEST</Button>
)
}
Contributing
We would love you to contribute to our project. It's simple:
- Create an issue with bug you found or proposal you have. Wait for approve from maintainer.
- Create a pull request. Make sure all checks are green.
- Fix review comments if any.
- Be awesome.
Installation for local development
After cloning this repo if you'd like to use it in another project using React
(placed at e.g. /path/to/your/awesome/project/
) you should follow these steps:
In ke
folder
install all deps:
yarn install
create link to
@bestdoctor/ke
:yarn link
link existing React and Chakra UI in order to prevent hooks problem
react
inke
node_modules
:cd node_modules/react yarn link cd ../@chakra-ui/react yarn link cd ../../react-query yarn link
In your-project-folder
install all deps:
yarn install
link all external dependencies and
ke
:yarn link react @chakra-ui/react @bestdoctor/ke react-query react-router-dom
Here are useful tips:
- You can run all checks and tests with
yarn makecheck
. Please do it before TravisCI does. - We respect Django CoC. Make soft, not bullshit.
Commit Message Guidelines
We have rules over how our git commit messages can be formatted. This leads to more readable messages that are easy to follow when looking through the project history. In general, the rules for describing development commits are here.
To check commits, we use @commitlint/config-conventional. I also recommend reading the Angular contributing guide if you want to learn more about regular commits and see examples.