Skip to content

Commit

Permalink
RocketChat#755 [EDIT] Доработан раздел "Рабочая группа" и "Справочник…
Browse files Browse the repository at this point in the history
… рабочих групп"
  • Loading branch information
Scarvis committed Nov 5, 2020
1 parent d039560 commit 65766b4
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 226 deletions.
4 changes: 2 additions & 2 deletions app/api/server/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ export class APIClass extends Restivus {
};
this.defaultLimitedUserFieldsToExclude = {
avatarOrigin: 0,
emails: 0,
phone: 0,
// emails: 0,
// phone: 0,
statusConnection: 0,
createdAt: 0,
lastLogin: 0,
Expand Down
1 change: 0 additions & 1 deletion app/ui/client/views/app/home.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ const toolbarButtons = () => [
{
name: t('Working_group'),
icon: 'working_group',
condition: () => hasPermission('manage-working-group'),
action: () => {
menu.close();
FlowRouter.go('working-group');
Expand Down
2 changes: 1 addition & 1 deletion app/working-group/client/views/WorkingGroups.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function WorkingGroups({

const renderRow = (userWorkingGroup) => {
const { _id, workingGroup, name, surname, patronymic, organization, position, emails, phone } = userWorkingGroup;
const email = emails[0].address ?? '';
const email = emails ? emails[0].address : '';
return <Table.Row key={_id} tabIndex={0} role='link' action>
<Table.Cell fontScale='p1' onClick={onClick(_id)} color='default'>{workingGroup}</Table.Cell>
<Table.Cell fontScale='p1' onClick={onClick(_id)} color='default'>{surname} {name} {patronymic}</Table.Cell>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { useState, useCallback } from 'react';
import { Field, TextAreaInput, Button, ButtonGroup } from '@rocket.chat/fuselage';
import { registerLocale } from 'react-datepicker';
import ru from 'date-fns/locale/ru';
registerLocale('ru', ru);

import { useToastMessageDispatch } from '../../../../../client/contexts/ToastMessagesContext';
import { useTranslation } from '../../../../../client/contexts/TranslationContext';
import { useMethod } from '../../../../../client/contexts/ServerContext';
import { validateWorkingGroupCompositionData, createWorkingGroupCompositionData } from '../lib';
import VerticalBar from '../../../../../client/components/basic/VerticalBar';

require('react-datepicker/dist/react-datepicker.css');

export function AddWorkingGroupComposition({ goToNew, close, onChange, ...props }) {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();

const [title, setTitle] = useState('');

const insertOrUpdateWorkingGroupComposition = useMethod('insertOrUpdateWorkingGroupComposition');

const saveAction = useCallback(async (title) => {
const workingGroupData = createWorkingGroupCompositionData(title);
const validation = validateWorkingGroupCompositionData(workingGroupData);
if (validation.length === 0) {
const _id = await insertOrUpdateWorkingGroupComposition(workingGroupData);
return _id;
}
validation.forEach((error) => { throw new Error({ type: 'error', message: t('error-the-field-is-required', { field: t(error) }) }); });
}, [dispatchToastMessage, insertOrUpdateWorkingGroupComposition, t]);

const handleSave = useCallback(async () => {
try {
const result = await saveAction(
title,
);
dispatchToastMessage({ type: 'success', message: t('Working_group_composition_added_successfully') });
goToNew();
close();
onChange();
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
}, [dispatchToastMessage, goToNew, close, onChange, saveAction, title, t]);

return <VerticalBar.ScrollableContent {...props}>
<Field>
<Field.Label>{t('Working_group')}</Field.Label>
<Field.Row>
<TextAreaInput size={'3'} value={title} onChange={(e) => setTitle(e.currentTarget.value)} placeholder={t('Working_group')} />
</Field.Row>
</Field>
<Field>
<Field.Row>
<ButtonGroup stretch w='full'>
<Button mie='x4' onClick={close}>{t('Cancel')}</Button>
<Button primary onClick={handleSave}>{t('Save')}</Button>
</ButtonGroup>
</Field.Row>
</Field>
</VerticalBar.ScrollableContent>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { useCallback, useState, useMemo, useEffect } from 'react';
import {
Box,
Button,
ButtonGroup,
Field,
Skeleton,
Throbber,
InputBox,
TextInput,
} from '@rocket.chat/fuselage';
import ru from 'date-fns/locale/ru';

import { registerLocale } from 'react-datepicker';
registerLocale('ru', ru);

import { useTranslation } from '../../../../../client/contexts/TranslationContext';
import { useMethod } from '../../../../../client/contexts/ServerContext';
import { useToastMessageDispatch } from '../../../../../client/contexts/ToastMessagesContext';
import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../../../../client/hooks/useEndpointDataExperimental';
import { validateWorkingGroupCompositionData, createWorkingGroupCompositionData } from '../lib';
import VerticalBar from '../../../../../client/components/basic/VerticalBar';

require('react-datepicker/dist/react-datepicker.css');

export function EditWorkingGroupComposition({ _id, cache, onChange, ...props }) {
const query = useMemo(() => ({
query: JSON.stringify({ _id }),
}), [_id, cache]);

const { data, state, error } = useEndpointDataExperimental('working-groups.list', query);

if (state === ENDPOINT_STATES.LOADING) {
return <Box pb='x20'>
<Skeleton mbs='x8'/>
<InputBox.Skeleton w='full'/>
<Skeleton mbs='x8'/>
<InputBox.Skeleton w='full'/>
<ButtonGroup stretch w='full' mbs='x8'>
<Button disabled><Throbber inheritColor/></Button>
<Button primary disabled><Throbber inheritColor/></Button>
</ButtonGroup>
<ButtonGroup stretch w='full' mbs='x8'>
<Button primary danger disabled><Throbber inheritColor/></Button>
</ButtonGroup>
</Box>;
}

if (error || !data || data.workingGroups.length < 1) {
return <Box fontScale='h1' pb='x20'>{error}</Box>;
}

return <EditWorkingGroupWithData workingGroupComposition={data.workingGroups[0]} onChange={onChange} {...props}/>;
}

function EditWorkingGroupWithData({ close, onChange, workingGroupComposition, ...props }) {
const t = useTranslation();
const dispatchToastMessage = useToastMessageDispatch();

const {
_id,
title: previousTitle,
} = workingGroupComposition || {};
const previousWorkingGroupComposition = workingGroupComposition || {};

const [title, setTitle] = useState(previousTitle);

useEffect(() => {
setTitle(previousTitle || '');
}, [
previousTitle,
_id]);

const insertOrUpdateWorkingGroupComposition = useMethod('insertOrUpdateWorkingGroupComposition');

const hasUnsavedChanges = useMemo(() => previousTitle !== title, [title]);

const saveAction = useCallback(async (title) => {
const workingGroupData = createWorkingGroupCompositionData(title,{ previousTitle, _id });
const validation = validateWorkingGroupCompositionData(workingGroupData);
if (validation.length === 0) {
const _id = await insertOrUpdateWorkingGroupComposition(workingGroupData);
}
validation.forEach((error) => { throw new Error({ type: 'error', message: t('error-the-field-is-required', { field: t(error) }) }); });
}, [_id, dispatchToastMessage, insertOrUpdateWorkingGroupComposition, title, previousTitle, previousWorkingGroupComposition]);

const handleSave = useCallback(async () => {
saveAction(title);
onChange();
}, [saveAction, onChange]);

return <VerticalBar.ScrollableContent {...props}>
<Field>
<Field.Label>{t('Title')}</Field.Label>
<Field.Row>
<TextInput value={title} onChange={(e) => setTitle(e.currentTarget.value)}/>
</Field.Row>
</Field>
<Field>
<Field.Row>
<ButtonGroup stretch w='full'>
<Button onClick={close}>{t('Cancel')}</Button>
<Button primary onClick={handleSave} disabled={!hasUnsavedChanges}>{t('Save')}</Button>
</ButtonGroup>
</Field.Row>
</Field>
</VerticalBar.ScrollableContent>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { useCallback, useMemo } from 'react';
import { Button, ButtonGroup, Icon, Modal, Table } from '@rocket.chat/fuselage';
import { useMediaQuery } from '@rocket.chat/fuselage-hooks';

import { useTranslation } from '../../../../../client/contexts/TranslationContext';
import { GenericTable, Th } from '../../../../../client/components/GenericTable';
import { useSetModal } from '../../../../../client/contexts/ModalContext';
import { useToastMessageDispatch } from '../../../../../client/contexts/ToastMessagesContext';
import { useMethod } from '../../../../../client/contexts/ServerContext';

const DeleteWarningModal = ({ onDelete, onCancel, ...props }) => {
const t = useTranslation();
return <Modal {...props}>
<Modal.Header>
<Icon color='danger' name='modal-warning' size={20}/>
<Modal.Title>{t('Are_you_sure')}</Modal.Title>
<Modal.Close onClick={onCancel}/>
</Modal.Header>
<Modal.Content fontScale='p1'>
{t('Working_group_delete_warning')}
</Modal.Content>
<Modal.Footer>
<ButtonGroup align='end'>
<Button ghost onClick={onCancel}>{t('Cancel')}</Button>
<Button primary danger onClick={onDelete}>{t('Delete')}</Button>
</ButtonGroup>
</Modal.Footer>
</Modal>;
};

const SuccessModal = ({ onClose, ...props }) => {
const t = useTranslation();
return <Modal {...props}>
<Modal.Header>
<Icon color='success' name='checkmark-circled' size={20}/>
<Modal.Title>{t('Deleted')}</Modal.Title>
<Modal.Close onClick={onClose}/>
</Modal.Header>
<Modal.Content fontScale='p1'>
{t('Working_group_has_been_deleted')}
</Modal.Content>
<Modal.Footer>
<ButtonGroup align='end'>
<Button primary onClick={onClose}>{t('Ok')}</Button>
</ButtonGroup>
</Modal.Footer>
</Modal>;
};

export function WorkingGroupCompositions({
data,
sort,
onEditClick,
onHeaderClick,
setParams,
onChange,
params,
}) {
const t = useTranslation();

const setModal = useSetModal();
const dispatchToastMessage = useToastMessageDispatch();

const deleteWorkingGroupComposition = useMethod('deleteWorkingGroupComposition');

const mediaQuery = useMediaQuery('(min-width: 768px)');

const onDeleteConfirm = useCallback(async (_id) => {
try {
await deleteWorkingGroupComposition(_id);
setModal(() => <SuccessModal title={'Delete'} onClose={() => { setModal(undefined); onChange(); }}/>);
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
}, [deleteWorkingGroupComposition, dispatchToastMessage, onChange]);

const onDel = (_id) => () => { onDeleteConfirm(_id); };

const onDeleteClick = (_id) => () => setModal(() => <DeleteWarningModal title={t('Council_Delete_Warning')} onDelete={onDel(_id)} onCancel={() => setModal(undefined)}/>);

const header = useMemo(() => [
mediaQuery && <Th key={'Working_group'} color='default'>
{t('Working_group')}
</Th>,
<Th w='x40' key='edit'></Th>,
<Th w='x40' key='delete'></Th>,
], [sort, mediaQuery]);

const renderRow = (WorkingGroupComposition) => {
const { _id, title } = WorkingGroupComposition;
return <Table.Row key={_id} tabIndex={0} role='link' action>
<Table.Cell fontScale='p1' color='default'>{title}</Table.Cell>
<Table.Cell alignItems={'end'}>
<Button small onClick={onEditClick(_id)} aria-label={t('Edit')}>
<Icon name='edit'/>
</Button>
</Table.Cell>
<Table.Cell alignItems={'end'}>
<Button small onClick={onDeleteClick(_id)} aria-label={t('Delete')}>
<Icon name='trash'/>
</Button>
</Table.Cell>
</Table.Row>;
};

return <GenericTable header={header} renderRow={renderRow} results={data} total={data.length} setParams={setParams} params={params} />;
}
Loading

0 comments on commit 65766b4

Please sign in to comment.