


# PremiseHQ Utility

Utility functions to help control PremiseHQ application flow



## ServerAPI

```jsx
import ServerAPI from 'phq-utility/lib/ServerAPI';
```
### APIs / Methods

#### `setVariables?: (variableObj: object) => void` 

Set `AppKey`, `Key`, `Host`, `Premise`, `$` through `variableObj`
```jsx
const Host = 'http://dev-nosql.premisehq.co';
const AppKey = 'cc3fdca374904ae89e3393779805efaf';
const Key = '2017asd0138dqwxiSBuPB/ZeTeWab1WIA==';

ServerAPI.setVariables({
    AppKey,
    Key,
    Host,
    $: window.jQuery // if applicable
})

```
Set `$` when jQuery isn't initialized globally

#### `getVariables?: () => object` 
Return an object with `AppKey`, `Key`, `Host`, `Premise`, `$` keys.
 
#### `getModuleData?: (queryObj: object, callBack: (res: object) => void) => object` 
Calls an API which performs query on a collection (module) based on `queryObj`.&nbsp;
`queryObj` accepts the following keys -
- `moduleName` - The targeted module
- `where`
    Check the following link to find out how to apply `query` on `find` function&nbsp;
    https://docs.mongodb.com/manual/reference/method/db.collection.find/
    Extent of implementation is still limited, everything might not work.&nbps;
    `Use $like instead of $regex and send Regex operator or a string when relevant query needs to be constructed`
- `rowDid`
    Use instead of `where` when the `Did` of the relevant row is known
- `select` - Array of fields that should be fetched
- `orderBy` - https://docs.mongodb.com/manual/reference/method/cursor.sort/#cursor.sort - Similar to how `sort` parameter is accepted there
- `limit` - A number to limit entries
- `skip` - A number to skip certain amount of initial entries

`callBack` is called once xhr request is complete.&nbsp;
Returned value is an ajax xhr object.

#### `modifyModuleData?: (queryObj: object, callBack: (res: object) => void) => object` 

Calls an API which performs query on a collection (module) based on `queryObj` and update the relevant rows.&nbsp;
`queryObj` accepts the following keys -
- `moduleName` - The targeted module
- `where`
    Check the following link to find out how to apply `query` on `find` function&nbsp;
    https://docs.mongodb.com/manual/reference/method/db.collection.find/
    Extent of implementation is still limited, everything might not work.&nbps;
    `Use $like instead of $regex and send Regex operator or a string when relevant query needs to be constructed`
- `rowDid`
    Use instead of `where` when the `Did` of the relevant row is known
- `data` - An object with id-value pairs. `id` corresponds to field names.
- `type` - `insert` | `update` | `delete` - based on the operation required
    
`callBack` is called once xhr request is complete.&nbsp;
Returned value is an ajax xhr object.
    
#### `rawAggregate?: (data: object, callBack: (res: object) => void) => object` 

Calls an API which performs aggregate on a collection (module) based on `data`&nbsp;
`data` accepts the following keys -
- `from` - The targeted module, we can also populate `moduleName` key instead
- `condition`
    Check the following link to find out what can be passed to a mongodb aggregate function&nbsp;
    https://docs.mongodb.com/manual/core/aggregation-pipeline/&nbsp;
    https://docs.mongodb.com/manual/meta/aggregation-quick-reference/&nbsp;
- `count` - Set `true` (boolean) if we're only interested in row count

`callBack` is called once xhr request is complete.&nbsp;
Returned value is an ajax xhr object.

#### `aggregate?: (data: object, callBack: (res: object) => void) => object` 

Extension on `rawAggregate`&nbsp;
`data` accepts the following keys -
- `from` - The targeted module, we can also populate `moduleName` key instead
- `where`
    Similar to how `$match` is constructed in an aggregate function&nbsp;
    https://docs.mongodb.com/manual/reference/operator/aggregation/match/
- `group`
    Similar to how `$group` is constructed in an aggregate function&nbsp;
    https://docs.mongodb.com/manual/reference/operator/aggregation/group/
- `orderBy`
    Similar to how `$sort` is constructed in an aggregate function&nbsp;
    https://docs.mongodb.com/manual/reference/operator/aggregation/sort/
- `skip`
    Similar to how `$skip` is constructed in an aggregate function&nbsp;
    https://docs.mongodb.com/manual/reference/operator/aggregation/skip/
- `limit`
    Similar to how `$limit` is constructed in an aggregate function&nbsp;
    https://docs.mongodb.com/manual/reference/operator/aggregation/limit/
- `count` - Set `true` (boolean) if we're only interested in row count
- `optimize`
    Set `true` if we require optimizing the amount of data sent. Should have `select` initialized and `group` uninitialized in such cases.&nbsp;
    In a regular scenario, `callBack` sends `res` where `res.Data` is stringified JSON array when `res.Status == "Success"`. When `optimize` is set `true`, the value of `res.Data` becomes a regular js Array.
- `maintainOrder`
    Function performs `where`, `group`, `orderBy`, `skip`, `limit` in the respective order if nothing is set. Set `maintainOrder` `true` if order should be maintained.

`callBack` is called once xhr request is complete.&nbsp;
Returned value is an ajax xhr object.

### Limitations / Gotchas
// TODO


## SnackBar

Built on top of [node-snackbar](https://www.npmjs.com/package/node-snackbar).

```jsx
import SnackBar from 'phq-utility/lib/ServerAPI';
```
### APIs / Methods / Attributes

#### `show?: (options: object) => string`

See [snackbar](https://www.polonel.com/snackbar/) to find out the available configurations for `options` object.&nbsp;
Returns a `token` which can be used to close that specific token.

#### `notify?: (textOrOptions: string | object, duration: number = 0) => string`

It uses the `show` function under the hood, but provides a shortcut access if only text and duration of a snackbar need configuration. In such case first send `text` in the first parameter, and `duration` in second. Instead of `text`, the `options` (which `show` function accepts in the first parameter) can also be used in the first parameter&nbsp; Has a default duration of `0`
Returns a `token` which can be used to close that specific token.

#### `warn?: (textOrOptions: string | object, duration: number = 8000) => string`

Similar to `notify` except it has a default `duration` period of `8000`ms.

#### `close?: (token: string) => boolean`

Closes the snackbar having the provided `token`. Dont send any arguments at all, if all existing snackbars are intended to be closed.

#### `current?: object`

Returns the current HTML DOM element containing the snackbar. `null` if no snackbar is present.

## Premise

```jsx
import Premise, { PurePremise } from 'phq-utility/lib/ServerAPI';

class Test extends Premise {
    ...

    componentDidMount () {
        this._requestHandler('aggregate', { from: 'Test', where: { TestField: '1' } }, res => {

        })
    }

    ...
}
```

### APIs

#### `_requestHandler?: (fn: string | Function, data: object, callBack: Function, config: object) => string | boolean`

- `fn`&nbsp;
    expects a function, we can also provide a string, in which case `fn` would be equal to ServerAPI[fn]. Here `ServerAPI` is the primary object exported by phq-utility. To fit regular structure, the function should have two parameters, one to receive some data, and another - a callback function, which should be invoked after request has been processed.&nbsp;&nbsp;

    `close` can be sent as the value of `fn`, to abort a request with certain token. 

- `data` - data is sent as the first parameter of that function, or send `token` value in case `fn` is equal to `close`
- `callBack` - callBack is sent as the second parameter of that function
- `config`&nbsp;
    * `onStart` => called immediately after passing the initial requisites, before any `delay` are applied
    * `delay` => request is invoked `delay` ms later
    * `beforeRequest` => called immediately before invoking the request function. Returning 'false' from this function prevents further execution
    * `forceCall` => request called even if the component is unmounted
    * `forceCallBack` => `callBack` (received from parameter) invoked even if the component is unmounted
    * `stayAlive` => the request isn't aborted even if the component is unmounted  
    * `params` => in case we wish to avoid the `data` and `callBack` parameter and send parameters differently, remember that last element of this array value will be considered a callBack
    * `onEnd` => called when request proceeding reaches its end

Returns a `token` which can be used to `close` the request. Returns `true` when `close` is used as `fn` and component was able to find the token and abort the request, else returns `false`.

#### `_snackBarHandler?: (fnStr: string, fnConfig: object, config: object, alternateConfig: object) => string | undefined`

- `fnStr`&nbsp;
    expects a string, would call `SnackBar`'s `fnStr` function afterwards&nbsp;&nbsp;
    `close` is a reserved value, providing `close` as `fnStr`, closes relevant SnackBar instance having a specific token. Token is provided through the following parameter `fnConfig`.

- `fnConfig` - Sent as the first argument of the invoked SnackBar function.

- `config`&nbsp;
    usually expects an object, possible attributes -
    * `forceCall` => `SnackBar` function called even if the component is unmounted
    * `stayAlive` => `SnackBar` instance isn't closed even if the component is unmounted

    In case value is not of type `object`, this is passed as the second argument of the invoked SnackBar function. Use `alternativeConfig` to pass configuration data then.

- `alternateConfig`&nbsp;
    in case `config` is not of type `object`, this becomes an acting `config`  

Returns a `token` which can be used to `close` the SnackBar. Returns result of SnackBar.close() `close` is used as `fnStr`.

#### `_stackRequestExecutor?: (requests: Array, shouldReject: Function, callBack: Function) => void`

// TODO

#### `_clear?: () => void`

Clears all requests and snackbar instances connected to Component


## docCookies

```jsx
import docCookies from 'phq-utility/lib/docCookies';
```

Exports the following framework&nbsp;
[docCookies](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie/Simple_document.cookie_framework)


## Loader

A simple react component to render loading interface

```jsx
import Loader from 'phq-utility/lib/Loader';
```

### Props

#### `style?: object`

Adds `style` to the container

#### `className?: string`

Adds `className` to the container

#### `dotSize?: string | number`

Size of the dots rendered inside (e.g. 10, '10px', '2em' etc)

#### `dotColor?: string`

Color of the dots

## Modal

```jsx
import Modal from 'phq-utility/lib/Modal';
```

### Props

#### `open?: boolean`

Show / hide modal

#### `onHideRequest?: Function`

Called when a request is made to hide the modal. At the moment, clicking on the overlay and pressing the `Esc` key trigger the function.

#### `rootStyle?: object`

Attaches style to root container

#### `innerContainerStyle?: object`

Attaches style to inner container

#### `style?: object`

Attaches style to immediate parent of `Modal`s children

#### `id?: string`

Attaches id to immediate parent of `Modal`s children

#### `portal?: boolean`

If reacts `createPortal` should be used to render the modal in a different place

#### `transitOnUnmount?: boolean`

If hiding transition should be applied even if the component is unmounted instead of setting `open` `false`. Might not have expected behavior if modal contains images with remote URL.

## Combo

A select component built on top of [react-select](https://www.npmjs.com/package/react-select) having bootstrap (.form-control) like styles out of the box.&nbsp;
Additional functions to support populating options on the fly as user searches providing an input.&nbsp;
`Doesn't cover all `react-select` props, `react-select` should be used directly if complete control is expected.`

```jsx
import Combo from 'phq-utility/lib/Combo';
```

### Props

#### `remote?: boolean`

If the `options` of the component should be fetched from a remote resource. By default its `true`.

#### `options?: Array`

Array of objects({ label, value }) to render possible options to select from. Is ignored when `remote` is set `true`.

#### `value?: object | Array`

Value of the component.

#### `placeholder?: string`

Placeholder of the component.

#### `onChange?: (value? object | Array, info: object) => void`

Triggered when value of the component changes.

#### `isLoading?: boolean`

Renders a loading UI.

#### `isMulti?: boolean`

If the user can select more than one option.

#### `isDisabled?: boolean`

Renders component is disabled mode.

#### `isCreatable?: boolean`

If creating new option not included in the options, supported.

#### `isClearable?: boolean`

If user can remove the selected value. By default set `true` if `remote` value is `true`.

#### `menuPortalTarget?: [object HTMLElement]`

In case component should be rendered through portal inside another HTML Element.

#### `allowCreateWhileLoading?: boolean`

See [react-select](https://react-select.com/props#creatable-props). Available when `isCreatable` is `true`.

#### `createOptionPosition?: boolean`

See [react-select](https://react-select.com/props#creatable-props). Available when `isCreatable` is `true`.

#### `formatCreateLabel?: boolean`

See [react-select](https://react-select.com/props#creatable-props). Available when `isCreatable` is `true`.

#### `isValidNewOption?: boolean`

See [react-select](https://react-select.com/props#creatable-props). Available when `isCreatable` is `true`.

#### `getNewOptionData?: boolean`

See [react-select](https://react-select.com/props#creatable-props). Available when `isCreatable` is `true`.

#### `onCreateOption?: boolean`

See [react-select](https://react-select.com/props#creatable-props). Available when `isCreatable` is `true`.

#### `collectorKey?: string | Symbol`

No-op if `remote` props is `false`. Use unique value for each type of combos where the components of that certain type fetch the same type of data as user continues search providing input. Reduces remote calls.&nbsp;
`Should be provided at the time of mounting and shouldn't be altered throughout its life cycle.`

#### `expiryPeriod?: number`

No-op if `remote` props is `false`. Amount of period in mili-seconds till component ignores searching remote options for the same input if `options` was already fetched before. By default its two minutes.&nbsp;
`Should be provided at the time of mounting and shouldn't be altered throughout its life cycle.`

#### `SelectStyles?: object`

See [react-select styles](https://react-select.com/styles).&nbsp;
`Should be provided at the time of mounting and shouldn't be altered throughout its life cycle.`

#### `fn?: (data: any, callBack: Function) => object`

No-op if `remote` props is `false`. The function that should be called when user searches for available options through input. Function should have a structure where the first parameter receives data and second parameter is used as a callback after request has been served. `Combo` expects this function to return an xhr-like object.&nbsp;

```jsx
import React, { Component, createElement as e } from 'react';
import Combo from 'phq-utility/lib/Combo';
import ServerAPI from 'phq-utility/lib/ServerAPI';

export default class SelectField extends Component {
    _onChange () {
        var { props } = this;
        var { onChange } = props;
        onChange && onChange.apply(null, [props].concat(Array.from(arguments)));
    }
    _onChange = this._onChange.bind(this);

    getRequestData (d) {
        const { props } = this;
        var { module, name, where } = props;
        return {
            from: module,
            where: Object.assign({
                [name]: {
                    $regex: d.text,
                    $options: 'i'
                }
            }, where || null),
            group: {
                _id: '$' + name,
                '0': {
                    $first: '$' + name
                },
            },
            skip: d.skip,
            limit: d.limit,
        }
    }
    getRequestData = this.getRequestData.bind(this);

    processResponse (r) {
        var res = r.response;
        if (res.Status == 'Success') {
            let options = [];
            JSON.parse(res.Data).forEach(i => {
                let v = i['0'];
                options.push({ label: v, value: v });
            })
            return { options };
        } else {
            // TODO
            // Handle exception
        }
    }
    processResponse = this.processResponse.bind(this);


    render () {
        const { props } = this;
        var { name, value, options, multiple, menuPortalTarget, style, className } = props;

        var comboProps = {
            ref: 'input',
            name,
            value,
            onChange: this._onChange,
            isMulti: multiple === true,
            isClearable: true,
            remote: options ? false : true,
            menuPortalTarget
        }

        if (options) {
            comboProps.options = options;
        } else {
            comboProps.getRequestData = this.getRequestData;
            comboProps.processResponse = this.processResponse;
            comboProps.fn = ServerAPI.aggregate;
        }

        if ('placeholder' in props) comboProps.placeholder = props.placeholder;

        return (
            <div style={style} className={className}>
                {e(Combo, comboProps)}
            </div>

        );
    }
}

```

#### `getRequestData?: (data: object) => any`

No-op if `remote` props is `false`. The function that is fed current `props`, `text`, `skip` & `limit` to receive returned result which will be used as the first argument of `fn` props function.&nbsp;

Check out the above example how `getRequestData` gets incorporated.


#### `processResponse?: (data: object) => object<{ options }>`

No-op if `remote` props is `false`. After receiving the response from xhr, an object is sent in the first parameter, having current props with an added key `response` - where the response received from the xhr is set. Expects an object to be returned where the `options` key is set as an `Array`, to be rendered as the current `options` of the component.&nbsp;

Check out the above example to find out how `processResponse` can be integrated.



## License

ISC

