Skip to content
/ ke Public

React admin framework done by and for backenders

License

Notifications You must be signed in to change notification settings

best-doctor/ke

Repository files navigation

ke

Build Status Maintainability Test Coverage

React admin framework done by and for backenders

ALPHA VERSION – NOT FOR PRODUCTION USE

Installation

$ yarn add @bestdoctor/ke-beta

Usage example

API Documentation Storybook

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-beta'
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:

As a result, your admin class will look like this:

// admin.tsx

import {
  BaseAdmin,
  BaseFilter,
  LinkWidget,
  ReadOnlyWidget,
  SelectWidget,
  ForeignKeySelectWidget,
} from '@bestdoctor/ke-beta';
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 class
  • provider – instance of your provider implementation
  • user – user object from your backend. Used for permissions and analytics system
  • analytcs – analytics object instance. See below for more details.
import { ResourceComposer } from '@bestdoctor/ke-beta';
import { ChakraProvider } from '@chakra-ui/react';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'provider';

const provider = new Provider()

const App = () => (
  <ChakraProvider>
    <BrowsserRouter>
      <ResourceComposer>
        <AdminResource
          name="patients"
          admin={new PatientAdmin()}
          provider={provider}
          user={{ name: 'name', permissions: ['admin_user'] }}
          analytics={analyticsInstnace}
        />
      </ResourceComposer>
    </BrowsserRouter>
  </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-beta'

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-beta'

...
{
  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-beta'

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-beta'

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-beta'

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-beta'

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-beta:

    yarn link
  • link existing React and Chakra UI in order to prevent hooks problem react in ke 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-beta 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.