Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/protocols' into RocketChat#755
Browse files Browse the repository at this point in the history
# Conflicts:
#	packages/rocketchat-i18n/i18n/ru.i18n.json
  • Loading branch information
Scarvis committed Nov 9, 2020
2 parents a104144 + 0dc10ef commit 8120473
Show file tree
Hide file tree
Showing 31 changed files with 1,920 additions and 50 deletions.
1 change: 1 addition & 0 deletions app/api/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ import './v1/councils';
import './v1/working-groups';
import './v1/working-group-meetings';
import './v1/upload-files';
import './v1/protocols';

export { API, APIClass, defaultRateLimiterOptions } from './api';
25 changes: 25 additions & 0 deletions app/api/server/lib/protocols.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Protocols } from '../../../models/server/raw';

export async function findProtocols({ query = {}, pagination: { offset, count, sort } }) {
const cursor = await Protocols.find(query, {
sort: sort || { time: 1 },
skip: offset,
limit: count,
});

const total = await cursor.count();

const protocols = await cursor.toArray();

return {
protocols,
count: protocols.length,
offset,
total,
};
}

export async function findProtocol(_id) {
const cursor = await Protocols.findOne({ _id });
return cursor;
}
30 changes: 30 additions & 0 deletions app/api/server/v1/protocols.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { API } from '../api';
import { findProtocols, findProtocol } from '../lib/protocols';
import { hasPermission } from '../../../authorization';

API.v1.addRoute('protocols.list', { authRequired: true }, {
get() {
if (!hasPermission(this.userId, 'manage-protocols')) {
return API.v1.unauthorized();
}

const { offset, count } = this.getPaginationItems();
const { sort, query } = this.parseJsonQuery();

return API.v1.success(Promise.await(findProtocols({
query,
pagination: {
offset,
count,
sort,
},
})));
},
});

API.v1.addRoute('protocols.findOne', { authRequired: true }, {
get() {
const { query } = this.parseJsonQuery();
return API.v1.success(Promise.await(findProtocol(query._id)));
},
});
1 change: 1 addition & 0 deletions app/authorization/server/startup.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Meteor.startup(function() {
{ _id: 'manage-own-outgoing-integrations', roles: ['admin'] },
{ _id: 'manage-own-incoming-integrations', roles: ['admin'] },
{ _id: 'manage-oauth-apps', roles: ['admin'] },
{ _id: 'manage-protocols', roles: ['admin', 'secretary'] },
{ _id: 'manage-selected-settings', roles: ['admin'] },
{ _id: 'manage-tags', roles: ['admin'] },
{ _id: 'manage-working-group', roles: ['admin', 'secretary'] },
Expand Down
2 changes: 2 additions & 0 deletions app/models/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import Tags from './models/Tags';
import Councils from './models/Councils';
import WorkingGroups from './models/WorkingGroups';
import WorkingGroupMeetings from './models/WorkingGroupMeetings';
import Protocols from './models/Protocols';

export { AppsLogsModel } from './models/apps-logs-model';
export { AppsPersistenceModel } from './models/apps-persistence-model';
Expand Down Expand Up @@ -98,4 +99,5 @@ export {
Councils,
WorkingGroups,
WorkingGroupMeetings,
Protocols,
};
127 changes: 127 additions & 0 deletions app/models/server/models/Protocols.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { Base } from './_Base';
import { ObjectID } from 'bson';

class Protocols extends Base {
constructor() {
super('protocols');
}

// INSERT
create(data) {
return this.insert(data);
}

// REMOVE
removeById(_id) {
return this.remove({ _id });
}

// UPDATE
updateProtocol(_id, data) {
data._updatedAt = new Date();
return this.update({ _id }, { $set: { ...data } });
}

createSection(protocolId, sectionData) {
const _id = new ObjectID().toHexString();
sectionData._id = _id;

const data = this.findOne({ _id: protocolId });
data.sections = data.sections ? [...data.sections, sectionData] : [sectionData];
data._updatedAt = new Date();
this.update({ _id: protocolId }, { $set: { ...data } });

return _id;
}

removeSectionById(protocolId, _id) {
const data = this.findOne({ _id: protocolId });

if (data.sections) {
data.sections = data.sections.filter(section => section._id !== _id);
data._updatedAt = new Date();
this.update({ _id: protocolId }, { $set: { ...data }});
}
}

updateSection(protocolId, sectionData) {
const data = this.findOne({ _id: protocolId });

if (data.sections) {
data.sections = data.sections.map((section) => {
if (section._id === sectionData._id) {
return { ...sectionData, items: section.items };
}
return section;
});

data._updatedAt = new Date();
this.update({ _id: protocolId }, { $set: { ...data }});
}

return sectionData._id;
}

createItem(protocolId, sectionId, item) {
const _id = new ObjectID().toHexString();
item._id = _id;

const data = this.findOne({ _id: protocolId });

if (data.sections) {
data._updatedAt = new Date();

data.sections.forEach((section) => {
if (section._id === sectionId) {
section.items = section.items ? [...section.items, item] : [item];
}
});

this.update({ _id: protocolId }, { $set: { ...data } })
}

return _id;
}

removeItemById(protocolId, sectionId, _id) {
const data = this.findOne({ _id: protocolId });

if (data.sections) {
data.sections = data.sections.map((section) => {
if (section._id === sectionId) {
section.items = section.items.filter(item => item._id !== _id);
}
return section;
});

data._updatedAt = new Date();
this.update({ _id: protocolId }, { $set: { ...data }});
}
}

updateItem(protocolId, sectionId, itemData) {
const data = this.findOne({ _id: protocolId });

if (data.sections) {
data.sections = data.sections.map((section) => {
if (section._id === sectionId) {
section.items = section.items.map((item) => {
if (item._id === itemData._id) {
return { ...itemData };
}
return item;
})
}
return section;
});

data._updatedAt = new Date();
this.update({ _id: protocolId }, { $set: { ...data }});
}

return itemData._id;
}

}

export default new Protocols();
5 changes: 5 additions & 0 deletions app/models/server/raw/Protocols.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { BaseRaw } from './BaseRaw';

export class ProtocolsRaw extends BaseRaw {

}
3 changes: 3 additions & 0 deletions app/models/server/raw/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ import WorkingGroupMeetingsModel from '../models/WorkingGroupMeetings';
import { WorkingGroupMeetingRaw } from './WorkingGroupMeetingRaw';
import UploadsFilesModel from '../models/Uploads';
import { UploadsFilesRaw } from './UploadsFilesRaw';
import ProtocolsModel from '../models/Protocols';
import { ProtocolsRaw } from './Protocols';

export const Permissions = new PermissionsRaw(PermissionsModel.model.rawCollection());
export const Roles = new RolesRaw(RolesModel.model.rawCollection());
Expand Down Expand Up @@ -95,3 +97,4 @@ export const Councils = new CouncilsRaw(CouncilsModel.model.rawCollection());
export const WorkingGroups = new WorkingGroupRaw(WorkingGroupsModel.model.rawCollection());
export const WorkingGroupMeetings = new WorkingGroupMeetingRaw(WorkingGroupMeetingsModel.model.rawCollection());
export const UploadFiles = new UploadsFilesRaw(UploadsFilesModel.model.rawCollection());
export const Protocols = new ProtocolsRaw(ProtocolsModel.model.rawCollection());
94 changes: 94 additions & 0 deletions app/protocols/client/views/AddItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React, { useState, useCallback } from 'react';
import { Field, TextAreaInput, Button, InputBox, ButtonGroup, TextInput } from '@rocket.chat/fuselage';
import DatePicker, { 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 { useRouteParameter } from '../../../../client/contexts/RouterContext';
import { useMethod } from '../../../../client/contexts/ServerContext';
import { validateItemData, createItemData } from './lib';
import VerticalBar from '../../../../client/components/basic/VerticalBar';

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

const [number, setNumber] = useState('');
const [name, setName] = useState('');
const [responsible, setResponsible] = useState('');
const [expireAt, setExpireAt] = useState('');

const protocolId = useRouteParameter('id');
const sectionId = useRouteParameter('sectionId');

const insertOrUpdateItem = useMethod('insertOrUpdateItem');

const saveAction = useCallback(async (number, name, responsible, expireAt) => {
const itemData = createItemData(number, name, responsible, expireAt);
const validation = validateItemData(itemData);
if (validation.length === 0) {
const _id = await insertOrUpdateItem(protocolId, sectionId, itemData);
return _id;
}
validation.forEach((error) => { throw new Error({ type: 'error', message: t('error-the-field-is-required', { field: t(error) }) }); });
}, [dispatchToastMessage, insertOrUpdateItem, t]);

const handleSave = useCallback(async () => {
try {
const result = await saveAction(
number,
name,
responsible,
expireAt
);
dispatchToastMessage({ type: 'success', message: t('Item_Added_Successfully') });
goToNew(sectionId, result)();
onChange();
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
}, [dispatchToastMessage, goToNew, number, name, responsible, expireAt, onChange, saveAction, t]);

return <VerticalBar.ScrollableContent {...props}>
<Field>
<Field.Label>{t('Item_Number')}</Field.Label>
<Field.Row>
<InputBox value={number} onChange={(e) => setNumber(e.currentTarget.value)} placeholder={t('Item_Number')} />
</Field.Row>
</Field>
<Field>
<Field.Label>{t('Item_Name')}</Field.Label>
<Field.Row>
<TextAreaInput rows='10' multiple value={name} onChange={(e) => setName(e.currentTarget.value)} placeholder={t('Item_Name')} />
</Field.Row>
</Field>
<Field>
<Field.Label>{t('Item_Responsible')}</Field.Label>
<Field.Row>
<TextInput value={responsible} onChange={(e) => setResponsible(e.currentTarget.value)} placeholder={t('Item_Responsible')} />
</Field.Row>
</Field>
<Field>
<Field.Label>{t('Item_ExpireAt')}</Field.Label>
<Field.Row>
<DatePicker
dateFormat='dd.MM.yyyy'
selected={expireAt}
onChange={(newDate) => setExpireAt(newDate)}
customInput={<TextInput />}
locale='ru'
/>
</Field.Row>
</Field>
<Field>
<Field.Row>
<ButtonGroup stretch w='full'>
<Button mie='x4' onClick={close}>{t('Cancel')}</Button>
<Button primary onClick={handleSave} disabled={number === '' || name === ''}>{t('Save')}</Button>
</ButtonGroup>
</Field.Row>
</Field>
</VerticalBar.ScrollableContent>;
}
Loading

0 comments on commit 8120473

Please sign in to comment.