README.md

CXBOX UI

CXBOX - Rapid Enterprise Level Application Development Platform

license

Description

CXBOX main purpose is to speed up development of typical Enterprise Level Application based on Spring Boot. A fixed contract with a user interface called Cxbox-UI allows backend developer to create typical interfaces providing just Json meta files. Full set of typical Enterprise Level UI components included - widgets, fields, layouts (views), navigation (screens).

Using CXBOX

  • download Intellij Plugin adds platform specific autocomplete, inspection, navigation and code generation features.
  • download Demo and follow README.md instructions. Feel free to use demo as template project to start your own projects

CXBOX UI

Main concepts

@cxbox-ui/core is a typescript library that includes set of prebuilt pieces of Redux ecosystem, to correctly interact with CXBox framework. It contains all parts to combine Redux store inside of your React application.

  • Actions
  • Reducers
  • Middlewares
  • Epics
    • RxJS methods for asynchronous interaction with CXBOX framework
  • Api
    • Wrapped in RxJS observables
  • Utilities
  • Interfaces
    • CXBOX interaction typescript contract

schema


Changelog

changelog


Getting started

The best way to start CXBox project is to clone CXBox-Demo and follow the README instructions


Manual installation

@cxbox-ui/core distributed in form of ESM* npm package:

yarn add @cxbox-ui/core

or

npm install @cxbox-ui/core

Several libraries are specified as peer dependencies and should be installed

  • react
  • react-dom
  • @reduxjs/toolkit
  • rxjs
  • redux-observable
  • axios

[!WARNING] CJS module system are no longer supported


Usage

Library proposes to use common way of configuring your Redux store with some nuances

How to configure default Redux store

Reducers

Proper way of creating reducers

@cxbox-ui/core exports all arguments for @reduxjs/toolkit createReducer method, such as initialState and createReducerBuilderManager instances of default CXBox reducers

import {reducers} from '@cxbox-ui/core'
import {createReducer} from '@reduxjs/toolkit'

const viewReducerBuilder = reducers
    .createViewReducerBuilderManager(reducers.initialViewState)
    .builder

const viewReducer = createReducer(reducers.initialViewState, viewReducerBuilder)

ReducerBuildManager implements default methods of createReducer builder callback argument, with removeCase and replaceCase

const viewReducerBuilder = reducers
    .createViewReducerBuilderManager(reducers.initialViewState)
    .removeCase('sampleCXBoxAction')
    .addCase('anotherSampleAction', () => {/** do something with state */})
    .replaceCase('someCXBoxActionToo', () => {/** in case of CXBox realization is not satisfying */})
    .builder

const viewReducer = createReducer(reducers.initialViewState, viewReducerBuilder)

More appropriate case samples in CXBox-Demo

Combine reducers

import {combineReducers, configureStore} from '@reduxjs/toolkit'

const rootReducer = combineReducers({
    screen: screenReducer,
    data: dataReducer,
    view: viewReducer,
    session: sessionReducer,
    router: routerReducer,
    notification: notificationReducer
})

const store = configureStore({
    reducers: rootReducer
})

Redux-observable middleware

To make this store work with CXBox backend, you should configure asynchronous interaction by applying preconfigured Redux-Observable Epics

It will take two steps

  • Configure Api Axios instance
import {Api} from '@cxbox-ui/core'
import Axios from 'axios'

const __AJAX_TIMEOUT__ = 900000
const __CLIENT_ID__: number = Date.now()

const HEADERS = { Pragma: 'no-cache', 'Cache-Control': 'no-cache, no-store, must-revalidate' }

const instance = axios.create({
    baseURL: __API__,
    timeout: __AJAX_TIMEOUT__,
    responseType: 'json',
    headers: {
        ...HEADERS,
        ...{ ClientId: __CLIENT_ID__ }
    }
})

const CXBoxApiInstance = new Api(instance)

You can also extend any of Api methods to use in your epics

import {Api} from '@cxbox-ui/core'

class ExtendedApi extends Api {
    getSomethingUnusual(thing: string) {
        return this.$api.get(thing)
    }
}

const CXBoxApiInstance = new ExtendedApi(axiosInstance)
import {epics} from '@cxbox-ui/core'
import {combineEpics, createEpicMiddleware} from 'redux-observable'
import {configureStore, getDefaultMiddleware} from '@reduxjs/toolkit'

// Typescript cast if you changed default CXBoxEpic type
const coreEpics = {...epics} as unknown as Record<string, RootEpic>

const rootEpic = combineEpics(Object.values(coreEpics))

const epicMiddleware = createEpicMiddleware({
    dependencies: {
        api: CXBoxApiInstance
    }
})

const store = configureStore({
    reducer: rootReducer,
    middleware: getDefaultMiddleware().concat(epicMiddleware)
})

epicMiddleware.run(rootEpic)

Epics

Feel free to add, but be careful to replace or remove some epics. They can call actions that causes chain effect, but if you sure in consequences, manipulate imported epics object as you want.

import {createAction} from '@reduxjs/toolkit'

const sampleAction = createAction<string>('sampleAction')

const sampleEpic: RootEpic = (action$, state$, {api}) =>
    action$.pipe(
        filter(sampleAction.match),
        switchMap(action => {
            const name = action.payload

            if (name) {
                api.doSomething(name)
            }

            return EMPTY
        }),
        catchError(error => {
            return utils.createApiErrorObservable(error)
        })
    )

const rootEpic = combineEpics(...Object.values(epics), sampleEpic)

Middlewares

@cxbox-ui/core also exports some middlewares, that not uses Redux-Observable way, but they still need to be applied

import {configureStore, getDefaultMiddleware} from '@reduxjs/toolkit'
import {middlewares} from '@cxbox-ui/core'

const store = configureStore({
    reducer: rootReducer,
    middleware: getDefaultMiddleware()
        .concat(...Object.values(middlewares))
        .concat(epicMiddleware)
})

Widget example

import React from 'react'
import { interfaces } from '@cxbox-ui/core'
import { useDispatch } from 'react-redux'
import { useAppSelector } from '@store'
import { actions } from '@cxbox-ui/core'
import InfoRow from './components/InfoRow'
import { Row } from 'antd'
import { useFlatFormFields } from '@hooks/useFlatFormFields'

interface FunnelProps {
    meta: FunnelWidgetMeta
}

function Funnel({ meta }: FunnelProps) {
    const { bcName } = meta
    const data = useAppSelector(state => state.data[bcName])
    const sortedData = data?.slice().sort(sorter)
    const funnelData = sortedData?.map(i => ({ id: i.funnelKey, value: i.amount }))
    const color = sortedData?.map(i => i.color) as Array<string>

    const legend: Types.LegendCfg = {
        position: 'right',
        layout: 'vertical',
        itemMarginBottom: 16,
        itemName: { style: { fontSize: 14, fontFamily: 'Roboto', fontWeight: 400 } },
        marker: (name, index) => ({
            symbol: 'circle',
            style: {
                fill: color[index]
            }
        })
    }
    const label: Label = {
        content: labelData => labelData.value,
        style: {
            fontSize: 20,
            fontFamily: 'Roboto',
            fontWeight: '700',
            fill: '#141F35'
        }
    }
    return (
        <div>
            <AntFunnel data={funnelData} xField="id" yField="value" color={color} conversionTag={false} legend={legend} label={label} />
        </div>
    )
}

function sorter(a: interfaces.DataItem, b: interfaces.DataItem) {
    return parseInt(b.amount as string) - parseInt(a.amount as string)
}

export default React.memo(Funnel)

Contributing

All contributions are welcomed, as even a minor pull request with grammar fixes or a single documentation update is of a significant help for us!
We promise to handle all PR reviews in a friendly and respectful manner.

Описание

Official mirror of https://github.com/CX-Box/cxbox-ui

Конвейеры
0 успешных
0 с ошибкой