This commit is contained in:
a.bouhuolia
2021-11-23 15:29:17 +02:00
46 changed files with 1461 additions and 57 deletions

View File

@@ -0,0 +1,80 @@
import React from 'react';
import intl from 'react-intl-universal';
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
import { Intent, Alert } from '@blueprintjs/core';
import { AppToaster } from 'components';
import { useDeleteRole } from 'hooks/query';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
* Role delete alert.
*/
function RoleDeleteAlert({
name,
// #withAlertStoreConnect
isOpen,
payload: { roleId },
// #withAlertActions
closeAlert,
}) {
const { mutateAsync: deleteRole, isLoading } = useDeleteRole();
// Handle cancel delete role alert.
const handleCancelDelete = () => {
closeAlert(name);
};
// Handle confirm delete role.
const handleConfirmDeleteRole = () => {
deleteRole(roleId)
.then(() => {
AppToaster.show({
message: intl.get('roles.permission_schema.delete.alert_message'),
intent: Intent.SUCCESS,
});
})
.catch(
({
response: {
data: { errors },
},
}) => {},
)
.finally(() => {
closeAlert(name);
});
};
return (
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'delete'} />}
icon="trash"
intent={Intent.DANGER}
isOpen={isOpen}
onCancel={handleCancelDelete}
onConfirm={handleConfirmDeleteRole}
loading={isLoading}
>
<p>
<FormattedHTMLMessage
id={
'roles.permission_schema.once_delete_this_role_you_will_able_to_restore_it'
}
/>
</p>
</Alert>
);
}
export default compose(
withAlertStoreConnect(),
withAlertActions,
)(RoleDeleteAlert);

View File

@@ -16,6 +16,7 @@ import ExpensesAlerts from '../Expenses/ExpensesAlerts';
import AccountTransactionsAlerts from '../CashFlow/AccountTransactions/AccountTransactionsAlerts';
import UsersAlerts from '../Preferences/Users/UsersAlerts';
import CurrenciesAlerts from '../Preferences/Currencies/CurrenciesAlerts';
import RolesAlerts from '../Preferences/Users/Roles/RolesAlerts';
export default [
...AccountsAlerts,
@@ -36,4 +37,5 @@ export default [
...AccountTransactionsAlerts,
...UsersAlerts,
...CurrenciesAlerts,
...RolesAlerts,
];

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { TransactionsLockingFormProvider } from './TransactionsLockingFormProvider';
import TransactionsLockingForm from './TransactionsLockingForm';
export default function TransactionsLockingDialogContent({
// #ownProps
dialogName,
}) {
return (
<TransactionsLockingFormProvider dialogName={dialogName}>
<TransactionsLockingForm />
</TransactionsLockingFormProvider>
);
}

View File

@@ -0,0 +1,50 @@
import React from 'react';
import { Intent, Button, Classes } from '@blueprintjs/core';
import { useFormikContext } from 'formik';
import { FormattedMessage as T } from 'components';
import { useTransactionLockingContext } from './TransactionsLockingFormProvider';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils';
/**
* Transactions locking floating actions.
*/
function TransactionsLockingFloatingActions({
// #withDialogActions
closeDialog,
}) {
// Formik context.
const { isSubmitting } = useFormikContext();
const { dialogName } = useTransactionLockingContext();
// Handle cancel button click.
const handleCancelBtnClick = (event) => {
closeDialog(dialogName);
};
return (
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button
disabled={isSubmitting}
onClick={handleCancelBtnClick}
style={{ minWidth: '75px' }}
>
<T id={'cancel'} />
</Button>
<Button
intent={Intent.PRIMARY}
loading={isSubmitting}
style={{ minWidth: '75px' }}
type="submit"
>
{<T id={'submit'} />}
</Button>
</div>
</div>
);
}
export default compose(withDialogActions)(TransactionsLockingFloatingActions);

View File

@@ -0,0 +1,48 @@
import React from 'react';
import moment from 'moment';
import { Intent } from '@blueprintjs/core';
import { Formik } from 'formik';
import intl from 'react-intl-universal';
import '../../../style/pages/TransactionsLocking/TransactionsLockingDialog.scss'
import { AppToaster } from 'components';
import { CreateTransactionsLockingFormSchema } from './TransactionsLockingForm.schema';
import { useTransactionLockingContext } from './TransactionsLockingFormProvider';
import TransactionsLockingFormContent from './TransactionsLockingFormContent';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils';
const defaultInitialValues = {
date: moment(new Date()).format('YYYY-MM-DD'),
reason: '',
};
/**
* Transactions Locking From.
*/
function TransactionsLockingForm({
// #withDialogActions
closeDialog,
}) {
const { dialogName } = useTransactionLockingContext();
// Initial form values.
const initialValues = {
...defaultInitialValues,
};
// Handles the form submit.
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {};
return (
<Formik
validationSchema={CreateTransactionsLockingFormSchema}
initialValues={initialValues}
onSubmit={handleFormSubmit}
component={TransactionsLockingFormContent}
/>
);
}
export default compose(withDialogActions)(TransactionsLockingForm);

View File

@@ -0,0 +1,13 @@
import * as Yup from 'yup';
import intl from 'react-intl-universal';
import { DATATYPES_LENGTH } from 'common/dataTypes';
const Schema = Yup.object().shape({
date: Yup.date().required().label(intl.get('date')),
reason: Yup.string()
.required()
.min(3)
.max(DATATYPES_LENGTH.TEXT)
.label(intl.get('reason')),
});
export const CreateTransactionsLockingFormSchema = Schema;

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { Form } from 'formik';
import TransactionsLockingFormFields from './TransactionsLockingFormFields';
import TransactionsLockingFloatingActions from './TransactionsLockingFloatingActions';
/**
* Transactions locking form content.
*/
export default function TransactionsLockingFormContent() {
return (
<Form>
<TransactionsLockingFormFields />
<TransactionsLockingFloatingActions />
</Form>
);
}

View File

@@ -0,0 +1,72 @@
import React from 'react';
import { FastField, ErrorMessage } from 'formik';
import { Classes, FormGroup, TextArea, Position } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import { FieldRequiredHint, FormattedMessage as T } from 'components';
import { useAutofocus } from 'hooks';
import {
inputIntent,
momentFormatter,
tansformDateValue,
handleDateChange,
} from 'utils';
/**
* Transactions locking form fields.
*/
export default function TransactionsLockingFormFields() {
const dateFieldRef = useAutofocus();
return (
<div className={Classes.DIALOG_BODY}>
{/*------------ Date -----------*/}
<FastField name={'date'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'date'} />}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="date" />}
minimal={true}
className={classNames(CLASSES.FILL, 'form-group--date')}
>
<DateInput
{...momentFormatter('YYYY/MM/DD')}
onChange={handleDateChange((formattedDate) => {
form.setFieldValue('date', formattedDate);
})}
value={tansformDateValue(value)}
popoverProps={{
position: Position.BOTTOM,
minimal: true,
}}
intent={inputIntent({ error, touched })}
inputRef={(ref) => (dateFieldRef.current = ref)}
/>
</FormGroup>
)}
</FastField>
{/*------------ reasons -----------*/}
<FastField name={'reason'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'reason'} />}
labelInfo={<FieldRequiredHint />}
className={'form-group--reason'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'reason'} />}
>
<TextArea
growVertically={true}
large={true}
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</div>
);
}

View File

@@ -0,0 +1,21 @@
import React from 'react';
import { DialogContent } from 'components';
const TransactionsLockingContext = React.createContext();
function TransactionsLockingFormProvider({ dialogName, ...props }) {
// State provider.
const provider = {
dialogName,
};
return (
<DialogContent>
<TransactionsLockingContext.Provider value={provider} {...props} />
</DialogContent>
);
}
const useTransactionLockingContext = () =>
React.useContext(TransactionsLockingContext);
export { TransactionsLockingFormProvider, useTransactionLockingContext };

View File

@@ -0,0 +1,30 @@
import React from 'react';
import { Dialog, DialogSuspense, FormattedMessage as T } from 'components';
import withDialogRedux from 'components/DialogReduxConnect';
import { compose } from 'utils';
const TransactionsLockingContent = React.lazy(() =>
import('./TransactionsLockingDialogContent'),
);
/**
* Transaction Locking dialog
*/
function TransactionsLockingDialog({ dialogName, payload = {}, isOpen }) {
return (
<Dialog
name={dialogName}
autoFocus={true}
title={<T id={'transactions_locking.dialog.label'} />}
canEscapeKeyClose={true}
isOpen={isOpen}
className={'dialog--transaction--locking'}
>
<DialogSuspense>
<TransactionsLockingContent dialogName={dialogName} />
</DialogSuspense>
</Dialog>
);
}
export default compose(withDialogRedux())(TransactionsLockingDialog);

View File

@@ -30,6 +30,9 @@ import withSettings from '../Settings/withSettings';
import { compose } from 'utils';
import withSettingsActions from '../Settings/withSettingsActions';
import { Can, AbilityContext } from '../../components/Abilities';
/**
* Items actions bar.
*/
@@ -57,6 +60,8 @@ function ItemsActionsBar({
// Items refresh action.
const { refresh } = useRefreshItems();
const { ability } = React.useContext(AbilityContext);
// History context.
const history = useHistory();
@@ -101,13 +106,14 @@ function ItemsActionsBar({
/>
<NavbarDivider />
<Button
className={Classes.MINIMAL}
icon={<Icon icon="plus" />}
text={<T id={'new_item'} />}
onClick={onClickNewItem}
/>
{/* <Can I="create" a="Item" ability={ability}> */}
<Button
className={Classes.MINIMAL}
icon={<Icon icon="plus" />}
text={<T id={'new_item'} />}
onClick={onClickNewItem}
/>
{/* </Can> */}
<AdvancedFilterPopover
advancedFilterProps={{
conditions: itemsFilterRoles,

View File

@@ -0,0 +1,10 @@
import React from 'react';
const RoleDeleteAlert = React.lazy(() =>
import('../../../Alerts/Roles/RoleDeleteAlert'),
);
/**
* Roles alerts
*/
export default [{ name: 'role-delete', component: RoleDeleteAlert }];

View File

@@ -0,0 +1,93 @@
import React from 'react';
import intl from 'react-intl-universal';
import { Formik } from 'formik';
import { defaultTo, sumBy, isEmpty } from 'lodash';
import 'style/pages/Preferences/Roles/Form.scss';
import { Intent } from '@blueprintjs/core';
import { AppToaster, FormattedMessage as T } from 'components';
import { CreateRolesFormSchema, EditRolesFormSchema } from './RolesForm.schema';
import { useRolesFormContext } from './RolesFormProvider';
import { transformToArray } from './utils';
import RolesFormContent from './RolesFormContent';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import { compose, transformToForm } from 'utils';
const defaultValues = {
role_name: '',
role_description: '',
permissions: {},
};
/**
* Preferences - Roles Form.
*/
function RolesForm({
// #withDashboardActions
changePreferencesPageTitle,
}) {
const {
isNewMode,
createRolePermissionMutate,
editRolePermissionMutate,
permissionSchema,
roleId,
} = useRolesFormContext();
// Initial values.
const initialValues = {
...defaultValues,
...transformToForm(permissionSchema, defaultValues),
};
React.useEffect(() => {
changePreferencesPageTitle(<T id={'roles.label'} />);
}, [changePreferencesPageTitle]);
const handleFormSubmit = (values, { setSubmitting }) => {
const permission = transformToArray(values);
const form = {
...values,
permissions: permission,
};
setSubmitting(true);
const onSuccess = () => {
AppToaster.show({
message: intl.get(
isNewMode
? 'roles.permission_schema.success_message'
: 'roles.permission_schema.upload_message',
),
intent: Intent.SUCCESS,
});
setSubmitting(false);
};
const onError = (errors) => {
setSubmitting(false);
};
if (isNewMode) {
createRolePermissionMutate(form).then(onSuccess).catch(onError);
} else {
editRolePermissionMutate([roleId, form]).then(onSuccess).catch(onError);
}
};
return (
<Formik
initialValues={initialValues}
validationSchema={isNewMode ? CreateRolesFormSchema : EditRolesFormSchema}
onSubmit={handleFormSubmit}
>
<RolesFormContent />
</Formik>
);
}
export default compose(withDashboardActions)(RolesForm);

View File

@@ -0,0 +1,17 @@
import * as Yup from 'yup';
import intl from 'react-intl-universal';
import { DATATYPES_LENGTH } from 'common/dataTypes';
const Schema = Yup.object().shape({
role_name: Yup.string().required().label(intl.get('roles.label.role_name')),
role_description: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
permissions: Yup.object().shape({
subject: Yup.string(),
ability: Yup.string(),
value: Yup.boolean(),
}),
});
export const CreateRolesFormSchema = Schema;
export const EditRolesFormSchema = Schema;

View File

@@ -0,0 +1,76 @@
import React from 'react';
import { useHistory } from 'react-router-dom';
import { ErrorMessage, FastField, Form, useFormikContext } from 'formik';
import {
Button,
FormGroup,
InputGroup,
Intent,
TextArea,
} from '@blueprintjs/core';
import { inputIntent } from 'utils';
import { FormattedMessage as T, FieldRequiredHint } from 'components';
import { useAutofocus } from 'hooks';
import { RolesPermissionList } from './components';
/**
* Preferences - Roles Form content.
*/
export default function RolesFormContent() {
const history = useHistory();
const { isSubmitting, values } = useFormikContext();
const roleNameFieldRef = useAutofocus();
const handleCloseClick = () => {
history.go(-1);
};
return (
<Form>
{/* ---------- name ---------- */}
<FastField name={'role_name'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'name'} />}
labelInfo={<FieldRequiredHint />}
className={'form-group--name'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="role_name" />}
inline={true}
>
<InputGroup
medium={true}
inputRef={(ref) => (roleNameFieldRef.current = ref)}
{...field}
/>
</FormGroup>
)}
</FastField>
{/* ---------- description ---------- */}
<FastField name={'role_description'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'description'} />}
className={'form-group--description'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'role_description'} />}
inline={true}
>
<TextArea growVertically={true} height={280} {...field} />
</FormGroup>
)}
</FastField>
<RolesPermissionList />
<div className={'card__footer'}>
<Button intent={Intent.PRIMARY} loading={isSubmitting} type="submit">
<T id={'save'} />
</Button>
<Button onClick={handleCloseClick} disabled={isSubmitting}>
<T id={'cancel'} />
</Button>
</div>
</Form>
);
}

View File

@@ -0,0 +1,14 @@
import React from 'react';
import RolesForm from './RolesForm';
import { RolesFormProvider } from './RolesFormProvider';
/**
* Roles Form page.
*/
export default function RolesFormPage() {
return (
<RolesFormProvider roleId={undefined}>
<RolesForm />
</RolesFormProvider>
);
}

View File

@@ -0,0 +1,77 @@
import React from 'react';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import _, { isArray } from 'lodash';
import {
useCreateRolePermissionSchema,
useEditRolePermissionSchema,
usePermissionsSchema,
useRolePermission,
} from 'hooks/query';
import PreferencesPageLoader from '../../../PreferencesPageLoader';
import { transformToObject } from './utils';
const RolesFormContext = React.createContext();
/**
* Roles Form page provider.
*/
function RolesFormProvider({ roleId, ...props }) {
// Create and edit roles mutations.
const { mutateAsync: createRolePermissionMutate } =
useCreateRolePermissionSchema();
const { mutateAsync: editRolePermissionMutate } =
useEditRolePermissionSchema();
const {
data: permissionsSchema,
isLoading: isPermissionsSchemaLoading,
isFetching: isPermissionsSchemaFetching,
} = usePermissionsSchema();
// const roleId = 6;
const { data: permission, isLoading: isPermissionLoading } =
useRolePermission(roleId, {
enabled: !!roleId,
});
const isNewMode = !roleId;
const permissionSchema = transformToObject(permission);
// Provider state.
const provider = {
isNewMode,
roleId,
permissionsSchema,
permissionSchema,
isPermissionsSchemaLoading,
isPermissionsSchemaFetching,
createRolePermissionMutate,
editRolePermissionMutate,
};
return (
<div
className={classNames(
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT,
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT_ROLES_FORM,
)}
>
<div className={classNames(CLASSES.CARD)}>
{isPermissionsSchemaLoading || isPermissionLoading ? (
<PreferencesPageLoader />
) : (
<RolesFormContext.Provider value={provider} {...props} />
)}
</div>
</div>
);
}
const useRolesFormContext = () => React.useContext(RolesFormContext);
export { RolesFormProvider, useRolesFormContext };

View File

@@ -0,0 +1,150 @@
import React from 'react';
import { Checkbox } from '@blueprintjs/core';
import styled from 'styled-components';
import { castArray } from 'lodash';
import { FastField, useFormikContext } from 'formik';
import { whenRtl, whenLtr } from 'utils/styled-components';
import { Icon, Hint, If, Choose } from 'components';
import { useRolesFormContext } from './RolesFormProvider';
const RoleLabelCheckbox = ({ subject, label, description }) => (
<>
<LabelCheckbox>
{/*------------- subject checbox ------------- */}
<FastField name={subject} type="checkbox">
{({ form: { setFieldValue, values }, field }) => (
<Checkbox
className={'block'}
inline={true}
label={label}
name={subject}
/>
)}
</FastField>
<p>{description}</p>
</LabelCheckbox>
</>
);
const AbilitiesList = ({ subject, abilities }) => {
return (
<AbilitieList>
{abilities?.map(({ key, label }) => (
<FastField name={`permissions.${subject}/${key}`} type="checkbox">
{({ form: { setFieldValue, values }, field }) => (
<Checkbox inline={true} label={label} {...field} />
)}
</FastField>
))}
</AbilitieList>
);
};
const ExtraAbilitiesList = ({ subject, extraAbilities }) => {
return extraAbilities?.map(({ key, label }) => (
<AbilitieList>
<FastField name={`permissions.${subject}/${key}`} type="checkbox">
{({ form: { setFieldValue, values }, field }) => (
<Checkbox inline={true} label={label} {...field} />
)}
</FastField>
</AbilitieList>
));
};
export const RolesPermissionList = () => {
const { permissionsSchema } = useRolesFormContext();
return (
<GroupList>
<BoxedGroupList>
{permissionsSchema.map(
({
subject,
subject_label,
description,
abilities,
extra_abilities,
}) => {
const extraAbilitiesList = Array.isArray(extra_abilities)
? extra_abilities
: [];
const abilitiesList = castArray(abilities) ? abilities : [];
return (
<React.Fragment>
<RoleList>
<RoleLabelCheckbox
subject={subject}
label={subject_label}
description={description}
/>
<AbilitiesList subject={subject} abilities={abilitiesList} />
<ExtraAbilitiesList
subject={subject}
extraAbilities={extraAbilitiesList}
/>
</RoleList>
</React.Fragment>
);
},
)}
</BoxedGroupList>
</GroupList>
);
};
const GroupList = styled.div`
list-style: none;
border: 1px solid #d2dce2;
border-radius: 6px;
font-size: 13px;
ul:first-child > li:last-child {
border-bottom: 0;
border-top: 0;
}
`;
const BoxedGroupList = styled.ul`
margin: 0;
list-style: none;
`;
const RoleList = styled.li`
display: block;
padding: 5px 10px;
margin: 0;
line-height: 20px;
border-bottom: 1px solid #e0e0e0;
`;
const LabelCheckbox = styled.label`
> * {
display: inline-block;
}
.block {
width: 220px;
padding: 2px 0;
font-weight: 500;
}
`;
const AbilitieList = styled.ul`
list-style: none;
/* margin-left: 12px; // 10px */
margin: 0px 10px 0px;
> li {
display: inline-block;
margin-top: 3px;
}
`;
const AbilitiesChildList = styled.li`
display: inline-block;
margin-top: 3px;
`;

View File

@@ -0,0 +1,27 @@
import { isEmpty } from 'lodash';
export const transformToArray = ({ permissions }) => {
return Object.keys(permissions).map((index) => {
const [value, key] = index.split('/');
return {
subject: value,
ability: key,
value: permissions[index],
};
});
};
export const transformToObject = ({ name, description, permissions }) => {
if (!isEmpty(permissions)) {
const output = {};
permissions.forEach((item) => {
output[`${item.subject}/${item.ability}`] = !!item.value;
});
return {
role_name: name,
role_description: description,
permissions: { ...output },
};
}
};

View File

@@ -0,0 +1,41 @@
import React from 'react';
import intl from 'react-intl-universal';
import { DataTable } from 'components';
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
import { useRolesTableColumns, ActionsMenu } from './components';
import withAlertsActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
/**
* Roles data table.
*/
function RolesDataTable({
// #withAlertsActions
openAlert,
}) {
const columns = useRolesTableColumns();
const handleDeleteRole = ({ id }) => {
openAlert('role-delete', { roleId: id });
};
// const Data = [{ name: 'AH', description: 'Description' }];
return (
<DataTable
columns={columns}
data={[]}
// loading={}
// progressBarLoading={}
TableLoadingRenderer={TableSkeletonRows}
ContextMenu={ActionsMenu}
payload={{
onDeleteRole: handleDeleteRole,
}}
/>
);
}
export default compose(withAlertsActions)(RolesDataTable);

View File

@@ -0,0 +1,18 @@
import React from 'react';
import intl from 'react-intl-universal';
import { RolesListProvider } from './RolesListProvider';
import RolesDataTable from './RolesDataTable';
/**
* Roles list.
*/
function RolesListPrefernces() {
return (
<RolesListProvider>
<RolesDataTable />
</RolesListProvider>
);
}
export default RolesListPrefernces;

View File

@@ -0,0 +1,28 @@
import React from 'react';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
// import {} from 'hooks/query';
const RolesListContext = React.createContext();
/**
* Roles list provider.
*/
function RolesListProvider({ ...props }) {
// Provider state.
const provider = {};
return (
<div
className={classNames(
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT,
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT_USERS,
)}
>
<RolesListContext.Provider value={provider} {...props} />
</div>
);
}
const useRolesContext = () => React.useContext(RolesListContext);
export { RolesListProvider, useRolesContext };

View File

@@ -0,0 +1,55 @@
import React from 'react';
import intl from 'react-intl-universal';
import { Intent, Button, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
import { safeCallback } from 'utils';
import { Icon, If } from 'components';
/**
* Context menu of roles.
*/
export function ActionsMenu({ payload: { onDeleteRole }, row: { original } }) {
return (
<Menu>
<MenuItem
icon={<Icon icon="pen-18" />}
text={intl.get('roles.edit_roles')}
/>
<MenuDivider />
<MenuItem
icon={<Icon icon="trash-16" iconSize={16} />}
text={intl.get('roles.delete_roles')}
onClick={safeCallback(onDeleteRole, original)}
intent={Intent.DANGER}
/>
</Menu>
);
}
/**
* Retrieve Roles table columns.
* @returns
*/
export function useRolesTableColumns() {
return React.useMemo(
() => [
{
id: 'name',
Header: intl.get('roles.column.name'),
accessor: 'name',
className: 'name',
width: '100',
disableSortBy: true,
},
{
id: 'description',
Header: intl.get('roles.column.description'),
accessor: 'description',
className: 'description',
width: '120',
disableSortBy: true,
},
],
[],
);
}

View File

@@ -26,11 +26,18 @@ function UsersPreferences({ openDialog }) {
<div className={classNames(CLASSES.CARD)}>
<div className={classNames(CLASSES.PREFERENCES_PAGE_TABS)}>
<Tabs animate={true} onChange={onChangeTabs}>
<Tab id="users" title={intl.get('users')} />
<Tab id="roles" title={intl.get('roles')} />
<Tab
id="users"
title={intl.get('users')}
panel={<PreferencesSubContent preferenceTab="users" />}
/>
<Tab
id="roles"
title={intl.get('roles')}
panel={<PreferencesSubContent preferenceTab="roles" />}
/>
</Tabs>
</div>
<PreferencesSubContent preferenceTab="users" />
</div>
</div>
);

View File

@@ -1,40 +1,41 @@
import React from 'react';
import {
Button,
Intent,
} from '@blueprintjs/core';
import { useHistory } from 'react-router-dom';
import { Button, Intent } from '@blueprintjs/core';
import { FormattedMessage as T } from 'components';
import Icon from 'components/Icon';
import withDialogActions from 'containers/Dialog/withDialogActions';
import {compose} from 'utils';
import { compose } from 'utils';
function UsersActions({
openDialog,
closeDialog,
}) {
function UsersActions({ openDialog, closeDialog }) {
const history = useHistory();
const onClickNewUser = () => {
openDialog('invite-user');
};
const onClickNewRole = () => {
history.push('/preferences/roles');
};
return (
<div className="preferences-actions">
<Button
icon={<Icon icon='plus' iconSize={12} />}
icon={<Icon icon="plus" iconSize={12} />}
onClick={onClickNewUser}
intent={Intent.PRIMARY}>
intent={Intent.PRIMARY}
>
<T id={'invite_user'} />
</Button>
<Button
icon={<Icon icon='plus' iconSize={12} />}
onClick={onClickNewUser}>
icon={<Icon icon="plus" iconSize={12} />}
onClick={onClickNewRole}
>
<T id={'new_role'} />
</Button>
</div>
);
}
export default compose(
withDialogActions,
)(UsersActions);
export default compose(withDialogActions)(UsersActions);

View File

@@ -0,0 +1,86 @@
import React from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import clsx from 'classnames';
import { TransactionsLockingProvider } from './TransactionsLockingProvider';
import { TransactionLockingContent } from './components';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils';
const DataTest = [
{
name: 'sales',
description:
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do',
},
{
name: 'purchases',
description:
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do',
},
{
name: 'financial',
description:
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do',
},
];
function Paragraph({ className, children }) {
return <p className={clsx('paragraph', className)}>{children}</p>;
}
/**
* Transactions locking list.
*/
function TransactionsLockingList({
// #withDialogActions
openDialog,
}) {
// Handle switch transactions locking.
const handleSwitchTransactionsLocking = () => {
openDialog('transactions-locking', {});
};
return (
<TransactionsLockingProvider>
<TransactionsLocking>
<TransactionsLockingParagraph>
<TransLockingDesc>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</TransLockingDesc>
Lock All Transactions At Once. {' '}
<Link to={'/'}> {''}Lock All Transactions At Once </Link>
</TransactionsLockingParagraph>
{DataTest.map(({ name, description }) => (
<TransactionLockingContent
name={name}
description={description}
onSwitch={handleSwitchTransactionsLocking}
/>
))}
</TransactionsLocking>
</TransactionsLockingProvider>
);
}
export default compose(withDialogActions)(TransactionsLockingList);
const TransactionsLocking = styled.div`
display: flex;
flex-direction: column;
padding: 32px;
max-width: 700px;
`;
const TransactionsLockingParagraph = styled(Paragraph)`
margin-bottom: 30px;
`;
const TransLockingTitle = styled.h2`
margin-bottom: 12px;
`;
const TransLockingDesc = styled.p``;

View File

@@ -0,0 +1,25 @@
import React from 'react';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
const TransactionsLockingContext = React.createContext();
/**
* Transactions locking data provider.
*/
function TransactionsLockingProvider({ ...props }) {
// Provider
const provider = {};
return (
<DashboardInsider
// loading={}
>
<TransactionsLockingContext.Provider value={provider} {...props} />
</DashboardInsider>
);
}
const useTransactionsLockingContext = () =>
React.useContext(TransactionsLockingContext);
export { TransactionsLockingProvider, useTransactionsLockingContext };

View File

@@ -0,0 +1,79 @@
import React from 'react';
import styled from 'styled-components';
import { Switch, FormGroup, Position } from '@blueprintjs/core';
import { Hint, Icon, FormattedMessage as T } from 'components';
export const TransactionLockingContent = ({ name, description, onSwitch }) => (
<TransactionLockingWrapp>
<TransactionsLockingcontent>
<TransLockingIcon>
<Icon icon="lock" iconSize={22} />
</TransLockingIcon>
<div className="block">
<TransLockingItemTitle>
<T id={name} />{' '}
<Hint
content={
'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do'
}
position={Position.BOTTOM_LEFT}
/>
</TransLockingItemTitle>
<TransLockingItemDesc>{description}</TransLockingItemDesc>
</div>
<FormGroup>
<Switch
large={true}
defaultChecked={false}
minimal={true}
className="ml2"
onChange={onSwitch}
/>
</FormGroup>
</TransactionsLockingcontent>
</TransactionLockingWrapp>
);
const TransactionLockingWrapp = styled.div`
display: flex;
align-items: center;
border-radius: 6px;
border: 1px solid #d1dee2;
padding: 14px 18px;
margin-bottom: 25px;
background: #fff;
div.block {
flex: 1 1 0;
margin-left: 20px;
width: 100%;
}
`;
const TransactionsLockingcontent = styled.div`
display: flex;
align-items: center;
flex: 1 1 0;
`;
const TransLockingItemTitle = styled.h1`
font-size: 18px;
margin: 0 0 8px;
line-height: 1;
font-weight: 600;
`;
const TransLockingItemDesc = styled.p`
margin-bottom: 0;
opacity: 0.8;
`;
const TransLockingIcon = styled.div`
border: 1px solid #d2dde2;
height: 50px;
width: 50px;
text-align: center;
line-height: 50px;
border-radius: 5px;
color: #8190ac;
`;