diff --git a/app/councils/client/views/Council.js b/app/councils/client/views/Council.js index 4c20a3a2a889..530c316dc7fc 100644 --- a/app/councils/client/views/Council.js +++ b/app/councils/client/views/Council.js @@ -1,5 +1,5 @@ -import React, { useMemo, useState } from 'react'; -import { Box, Button, Field, Icon, Label, Table, TextInput, TextAreaInput } from '@rocket.chat/fuselage'; +import React, { useCallback, useMemo, useState } from 'react'; +import { ButtonGroup, Button, Field, Icon, Label, Table, TextInput, TextAreaInput, Modal } from '@rocket.chat/fuselage'; import Page from '../../../../client/components/basic/Page'; import { useTranslation } from '../../../../client/contexts/TranslationContext'; @@ -11,9 +11,50 @@ import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; import { useMethod } from '../../../../client/contexts/ServerContext'; import { settings } from '../../../../app/settings/client'; import moment from 'moment'; +import { useSetModal } from '/client/contexts/ModalContext'; +import { useToastMessageDispatch } from '/client/contexts/ToastMessagesContext'; const style = { textOverflow: 'ellipsis', overflow: 'hidden' }; +const DeleteWarningModal = ({ title, onDelete, onCancel, ...props }) => { + const t = useTranslation(); + return + + + {t('Are_you_sure')} + + + + {title} + + + + + + + + ; +}; + +const SuccessModal = ({ title, onClose, ...props }) => { + const t = useTranslation(); + return + + + {t('Deleted')} + + + + {title} + + + + + + + ; +}; + export function CouncilPage() { const t = useTranslation(); const formatDateAndTime = useFormatDateAndTime(); @@ -30,6 +71,12 @@ export function CouncilPage() { const invitedUsers = data.invitedUsers || { }; + const setModal = useSetModal(); + + const deleteCouncil = useMethod('deleteCouncil'); + + const dispatchToastMessage = useToastMessageDispatch(); + const mediaQuery = useMediaQuery('(min-width: 768px)'); const downloadCouncilParticipantsMethod = useMethod('downloadCouncilParticipants'); @@ -58,14 +105,34 @@ export function CouncilPage() { mediaQuery && {t('Council_Contact_person')}, mediaQuery && {t('Phone_number')}, mediaQuery && {t('Email')}, - mediaQuery && {t('Joined_at')} + mediaQuery && {t('Joined_at')}, ], [mediaQuery]); const styleTableRow = { wordWrap: 'break-word' }; + const getBackgroundColor = (invitedUser) => { + const index = invitedUsers.findIndex((user) => ( + user.firstName === invitedUser.firstName + && user.lastName === invitedUser.lastName + && user.patronymic === invitedUser.patronymic + && user.position === invitedUser.position + && user.contactPersonFirstName === invitedUser.contactPersonFirstName + && user.contactPersonLastName === invitedUser.contactPersonLastName + && user.contactPersonPatronymicName === invitedUser.contactPersonPatronymicName + && user.phone === invitedUser.phone + && user.email === invitedUser.email + && user.ts === invitedUser.ts + )); + if (index > 0 && index % 2 === 1) { + return 'var(--color-lighter-blue)'; + } + + return ''; + }; + const renderRow = (invitedUser) => { const iu = invitedUser; - return + return {iu.lastName} {iu.firstName} {iu.patronymic} {iu.position} { mediaQuery && {iu.contactPersonLastName} {iu.contactPersonFirstName} {iu.contactPersonPatronymicName}} @@ -79,6 +146,30 @@ export function CouncilPage() { window.history.back(); }; + const goToCouncils = () => { + FlowRouter.go('councils'); + }; + + const onEdit = (_id) => () => { + FlowRouter.go(`/council/edit/${ _id }`); + }; + + const onNewParticipantClick = (_id) => () => { + FlowRouter.go(`/council/edit/newParticipant/${ _id }`); + }; + + const onDeleteCouncilConfirm = useCallback(async () => { + try { + await deleteCouncil(councilId); + setModal(() => { setModal(undefined); }}/>); + goToCouncils(); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + } + }, [deleteCouncil, dispatchToastMessage]); + + const onDeleteCouncilClick = () => setModal(() => setModal(undefined)}/>); + return @@ -87,7 +178,16 @@ export function CouncilPage() { + + + + + @@ -99,7 +199,7 @@ export function CouncilPage() { {t('Description')} - {data.desc} + @@ -109,14 +209,16 @@ export function CouncilPage() { - - {t('Council_Invited_Users')} + + - + ; diff --git a/app/councils/client/views/Councils.js b/app/councils/client/views/Councils.js index d79b958980b9..c67fa7949c09 100644 --- a/app/councils/client/views/Councils.js +++ b/app/councils/client/views/Councils.js @@ -1,5 +1,5 @@ -import React, { useMemo } from 'react'; -import { Box, Button, Icon, Table } from '@rocket.chat/fuselage'; +import React, { useCallback, useMemo } from 'react'; +import { Box, Button, ButtonGroup, Icon, Modal, Table } from '@rocket.chat/fuselage'; import { useMediaQuery } from '@rocket.chat/fuselage-hooks'; import { useTranslation } from '../../../../client/contexts/TranslationContext'; @@ -7,6 +7,47 @@ import { GenericTable, Th } from '../../../../client/components/GenericTable'; import { useFormatDateAndTime } from '../../../../client/hooks/useFormatDateAndTime'; import { useMethod } from '../../../../client/contexts/ServerContext'; import moment from 'moment'; +import { useSetModal } from '/client/contexts/ModalContext'; +import { useToastMessageDispatch } from '/client/contexts/ToastMessagesContext'; + +const DeleteWarningModal = ({ title, onDelete, onCancel, ...props }) => { + const t = useTranslation(); + return + + + {t('Are_you_sure')} + + + + {title} + + + + + + + + ; +}; + +const SuccessModal = ({ title, onClose, ...props }) => { + const t = useTranslation(); + return + + + {t('Deleted')} + + + + {title} + + + + + + + ; +}; export function Councils({ data, @@ -14,11 +55,18 @@ export function Councils({ onClick, onEditClick, onHeaderClick, + onChange, setParams, params, }) { const t = useTranslation(); + const setModal = useSetModal(); + + const deleteCouncil = useMethod('deleteCouncil'); + + const dispatchToastMessage = useToastMessageDispatch(); + const mediaQuery = useMediaQuery('(min-width: 768px)'); const downloadCouncilParticipantsMethod = useMethod('downloadCouncilParticipants'); @@ -78,6 +126,7 @@ export function Councils({ {t('Description')}, mediaQuery && {t('Created_at')}, , + , ], [sort, mediaQuery]); @@ -85,6 +134,19 @@ export function Councils({ const styleTr = { borderBottomWidth: '10px', borderBottomColor: 'var(--color-white)' }; + const onDeleteCouncilConfirm = useCallback(async (_id) => { + try { + await deleteCouncil(_id); + setModal(() => { setModal(undefined); onChange(); }}/>); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + } + }, [deleteCouncil, dispatchToastMessage, onChange]); + + const onDel = (_id) => () => { onDeleteCouncilConfirm(_id); }; + + const onDeleteCouncilClick = (_id) => () => setModal(() => setModal(undefined)}/>); + const renderRow = (council) => { const { _id, d: date, desc, ts } = council; return @@ -96,6 +158,11 @@ export function Councils({ + + + - - - - - - ; + query: JSON.stringify({ _id: councilId }), + }), [councilId]); + + const { data } = useEndpointDataExperimental('councils.findOne', query) || { result: [] }; + const [cache, setCache] = useState(); + + const onChange = useCallback(() => { + setCache(new Date()); + }, []); + + if (!data) { + return {'error'}; } - if (error || !data || data.councils.length < 1) { - return {error}; + if (!data.invitedUsers) { + data.invitedUsers = []; } - return ; + const getNewData = () => { + return { + d: new Date(), + desc: '', + invitedUsers: [], + }; + }; + + console.log(data); + + return ; } -function EditCouncilWithData({ close, onChange, council, ...props }) { +EditCouncilPage.displayName = 'EditCouncilPage'; + +export default EditCouncilPage; + +const style = { textOverflow: 'ellipsis', overflow: 'hidden' }; + +function EditCouncilWithNewData({ council, onChange, context }) { const t = useTranslation(); - const dispatchToastMessage = useToastMessageDispatch(); const { _id, d: previousDate, desc: previousDescription } = council || {}; + const previousInvitedUsers = useMemo(() => council.invitedUsers ? council.invitedUsers.slice() : [], [council.invitedUsers.slice()]); const previousCouncil = council || {}; + const isEditUserState = context === 'newParticipant'; + const [isEditUser, setIsEditUser] = useState(isEditUserState); const [date, setDate] = useState(new Date(previousDate)); const [description, setDescription] = useState(previousDescription); + const [invitedUsers, setInvitedUsers] = useState(previousInvitedUsers); + const [currentInvitedUser, setCurrentInvitedUser] = useState({}); + const setModal = useSetModal(); + const insertOrUpdateCouncil = useMethod('insertOrUpdateCouncil'); + const deleteCouncil = useMethod('deleteCouncil'); + + const dispatchToastMessage = useToastMessageDispatch(); + useEffect(() => { setDate(new Date(previousDate) || ''); setDescription(previousDescription || ''); }, [previousDate, previousDescription, previousCouncil, _id]); - const deleteCouncil = useMethod('deleteCouncil'); - const insertOrUpdateCouncil = useMethod('insertOrUpdateCouncil'); + const compare = (arr1, arr2) => { return arr1.length === arr2.length && arr1.every((v, i) => ( + v.firstName === arr2[i].firstName + && v.lastName === arr2[i].lastName + && v.patronymic === arr2[i].patronymic + && v.position === arr2[i].position + && v.contactPersonFirstName === arr2[i].contactPersonFirstName + && v.contactPersonLastName === arr2[i].contactPersonLastName + && v.contactPersonPatronymicName === arr2[i].contactPersonPatronymicName + && v.phone === arr2[i].phone + && v.email === arr2[i].email + && v.ts === arr2[i].ts)); + }; + + const goBack = () => { + window.history.back(); + }; + + const goToCouncils = () => { + FlowRouter.go('councils'); + }; + + // console.log(compare(previousInvitedUsers, invitedUsers)); + // console.log(previousInvitedUsers); + // console.log(invitedUsers); + // console.log(council.invitedUsers); + // console.log(description); + // console.log(previousDescription); + // console.log(council.desc); + + const hasUnsavedChanges = useMemo(() => new Date(previousDate).getTime() !== new Date(date).getTime() || previousDescription !== description || !compare(previousInvitedUsers, invitedUsers), + [date, description, invitedUsers, previousDate, previousDescription, previousInvitedUsers]); + + const hasEditUser = useMemo(() => isEditUser === true, [isEditUser]); - const hasUnsavedChanges = useMemo(() => previousDate !== date || previousDescription !== description, - [date, description]); + const handleEditUser = () => { + setIsEditUser(!isEditUser); + }; - const saveAction = useCallback(async (date, description) => { - const councilData = createCouncilData(date, description, { previousDate, previousDescription, _id }); + const handleAddUser = () => { + setCurrentInvitedUser({}); + handleEditUser(); + }; + + const resetData = () => { + setDate(new Date(previousDate)); + setDescription(previousDescription); + setInvitedUsers(previousInvitedUsers); + onChange(); + }; + + const getIndexInvitedUser = (invitedUser) => { + return invitedUsers.findIndex((user) => ( + user.firstName === invitedUser.firstName + && user.lastName === invitedUser.lastName + && user.patronymic === invitedUser.patronymic + && user.position === invitedUser.position + && user.contactPersonFirstName === invitedUser.contactPersonFirstName + && user.contactPersonLastName === invitedUser.contactPersonLastName + && user.contactPersonPatronymicName === invitedUser.contactPersonPatronymicName + && user.phone === invitedUser.phone + && user.email === invitedUser.email + && user.ts === invitedUser.ts + )); + }; + + const onEditClick = (invitedUser) => () => { + setCurrentInvitedUser(invitedUser); + onChange(); + handleEditUser(); + }; + + const handleInsertOrUpdatePerson = (newUser, oldUser) => () => { + const indexOldUser = getIndexInvitedUser(oldUser); + if (indexOldUser > -1) { + invitedUsers[indexOldUser] = newUser; + } else { + invitedUsers.push(newUser); + } + setCurrentInvitedUser({}); + onChange(); + handleEditUser(); + }; + + const saveAction = useCallback(async (date, description, invitedUsers) => { + const councilData = createCouncilData(date, description, { previousDate, previousDescription, _id }, invitedUsers); const validation = validate(councilData); if (validation.length === 0) { const _id = await insertOrUpdateCouncil(councilData); + goBack(); } validation.forEach((error) => { throw new Error({ type: 'error', message: t('error-the-field-is-required', { field: t(error) }) }); }); - }, [_id, dispatchToastMessage, insertOrUpdateCouncil, date, description, previousDate, previousDescription, previousCouncil, t]); + }, [_id, dispatchToastMessage, insertOrUpdateCouncil, date, description, invitedUsers, previousDate, previousDescription, previousCouncil, t]); - const handleSave = useCallback(async () => { - saveAction(date, description); + const handleSaveCouncil = useCallback(async () => { + await saveAction(date, description, invitedUsers); + dispatchToastMessage({ type: 'success', message: t('Council_edited') }); onChange(); }, [saveAction, onChange]); - const onDeleteConfirm = useCallback(async () => { + const handleDeleteCouncil = useCallback(async () => { try { await deleteCouncil(_id); - setModal(() => { setModal(undefined); close(); onChange(); }}/>); + setModal(() => { setModal(undefined); onChange(); }}/>); + onChange(); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); onChange(); } - }, [_id, close, deleteCouncil, dispatchToastMessage, onChange]); - - const openConfirmDelete = () => setModal(() => setModal(undefined)}/>); - - return - - {t('Date')} - - setDate(newDate)} - showTimeSelect - timeFormat='HH:mm' - timeIntervals={5} - timeCaption='Время' - customInput={} - locale='ru' - /> - {/* setDate(e.currentTarget.value)} placeholder={t('Date')} />*/} - - - - {t('Description')} - - setDescription(e.currentTarget.value)} placeholder={t('Description')} /> - - - - - - - + }, [deleteCouncil, onChange]); + + const onDeleteParticipantConfirm = (invitedUser) => () => { + try { + const indexUser = getIndexInvitedUser(invitedUser); + if (indexUser < 0) { + dispatchToastMessage({ type: 'error', message: t('User_not_found') }); + return; + } + invitedUsers.splice(indexUser, 1); + setModal(() => { setModal(undefined); close(); onChange(); }}/>); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + onChange(); + } + }; + + const onDeleteCouncilConfirm = useCallback(async () => { + try { + await handleDeleteCouncil(); + goToCouncils(); + } catch (error) { + dispatchToastMessage({ type: 'error', message: error }); + onChange(); + } + }, [handleDeleteCouncil, dispatchToastMessage, onChange]); + + const onDeleteParticipantClick = (invitedUser) => () => setModal(() => setModal(undefined)}/>); + + const onDeleteCouncilClick = () => setModal(() => setModal(undefined)}/>); + + return + + + + + + + + + + + - - - - - - + + + + {t('Date')} {t('Editing')} + + setDate(newDate)} + showTimeSelect + timeFormat='HH:mm' + timeIntervals={5} + timeCaption='Время' + customInput={} + locale='ru' + /> + + + + {t('Description')} {t('Editing')} + + setDescription(e.currentTarget.value)} placeholder={t('Description')} /> + + + + + { isEditUser && } + { !isEditUser && } + + + + + ; +} + +function EditInvitedUser({ invitedUser, handleCancel, handleInsertOrUpdateSubmit }) { + const [newData, setNewData] = useState({ + firstName: { value: invitedUser.firstName ?? '', required: true }, + lastName: { value: invitedUser.lastName ?? '', required: true }, + patronymic: { value: invitedUser.patronymic ?? '', required: false }, + position: { value: invitedUser.position ?? '', required: true }, + contactPersonFirstName: { value: invitedUser.contactPersonFirstName ?? '', required: false }, + contactPersonLastName: { value: invitedUser.contactPersonLastName ?? '', required: false }, + contactPersonPatronymicName: { value: invitedUser.contactPersonPatronymicName ?? '', required: false }, + phone: { value: invitedUser.phone ?? '', required: true }, + email: { value: invitedUser.email ?? '', required: true }, + ts: { value: invitedUser.ts ?? '', required: false }, + }); + + const isContact = !!(invitedUser.contactPersonFirstName && invitedUser.contactPersonLastName); + + const [isContactPerson, setIsContactPerson] = useState(isContact); + + const handleChange = (field, getValue = (e) => e.currentTarget.value) => (e) => { + setNewData({ ...newData, [field]: { value: getValue(e), required: newData[field].required } }); + }; + + const packNewData = () => { + const dataToSend = {}; + Object.keys(newData).forEach((key) => { + dataToSend[key] = newData[key].value.trim(); + }); + if (!isContactPerson) { + delete dataToSend.contactPersonFirstName; + delete dataToSend.contactPersonLastName; + delete dataToSend.contactPersonPatronymicName; + } + if (!dataToSend.ts) { + dataToSend.ts = new Date(); + } + return dataToSend; + }; + + const handleIAmContactPerson = () => { + setNewData({ + ...newData, + contactPersonFirstName: { value: newData.contactPersonFirstName.value, required: !isContactPerson }, + contactPersonLastName: { value: newData.contactPersonLastName.value, required: !isContactPerson }, + }); + setIsContactPerson(!isContactPerson); + }; + + const t = useTranslation(); + + const allFieldAreFilled = useMemo(() => Object.values(newData).filter((current) => current.value === '' && current.required === true).length === 0, [JSON.stringify(newData)]); + + console.log('allFieldAreFilled', allFieldAreFilled); + + const inputsStyle = { width: '99%' }; + + return + + + {t('Council_participant_info_description')} + + + - + + + + + {t('Council_second_name')} * + + + + + + {t('Council_first_name')} * + + + + + + {t('Council_patronymic')} + + + + + + {t('Council_Organization_Position')} * + + + + + + + {t('Council_Is_Contact_person')} + + { isContactPerson && + {t('Council_Contact_person_lastname')} * + + + + } + { isContactPerson && + {t('Council_Contact_person_firstname')} * + + + + } + { isContactPerson && + {t('Council_Contact_person_patronymic')} + + + + } + + {t('Council_Contact_person_Phone_number')} * + + + + + + {t('Council_Contact_person_email')} * + + + + + + + + ; +} + +function InvitedUsersTable({ invitedUsers, onEdit, onDelete }) { + const t = useTranslation(); + const formatDateAndTime = useFormatDateAndTime(); + + const [params, setParams] = useState({ current: 0, itemsPerPage: 25 }); + + const mediaQuery = useMediaQuery('(min-width: 768px)'); + + const header = useMemo(() => [ + {t('Council_participant')}, + {t('Council_Organization_Position')}, + mediaQuery && {t('Council_Contact_person')}, + mediaQuery && {t('Phone_number')}, + mediaQuery && {t('Email')}, + mediaQuery && {t('Joined_at')}, + , + , + ], [mediaQuery]); + + const styleTableRow = { wordWrap: 'break-word' }; + + const getBackgroundColor = (invitedUser) => { + const index = invitedUsers.findIndex((user) => ( + user.firstName === invitedUser.firstName + && user.lastName === invitedUser.lastName + && user.patronymic === invitedUser.patronymic + && user.position === invitedUser.position + && user.contactPersonFirstName === invitedUser.contactPersonFirstName + && user.contactPersonLastName === invitedUser.contactPersonLastName + && user.contactPersonPatronymicName === invitedUser.contactPersonPatronymicName + && user.phone === invitedUser.phone + && user.email === invitedUser.email + && user.ts === invitedUser.ts + )); + if (index > 0 && index % 2 === 1) { + return 'var(--color-lighter-blue)'; + } + + return ''; + }; + + const renderRow = (invitedUser) => { + const iu = invitedUser; + return + {iu.lastName} {iu.firstName} {iu.patronymic} + {iu.position} + { mediaQuery && {iu.contactPersonLastName} {iu.contactPersonFirstName} {iu.contactPersonPatronymicName}} + { mediaQuery && {iu.phone}} + { mediaQuery && {iu.email}} + { mediaQuery && {formatDateAndTime(iu.ts)}} + + + + + + + ; + }; + + return + + - ; + ; } diff --git a/app/councils/client/views/index.js b/app/councils/client/views/index.js index b74324894266..8c4b75146d51 100644 --- a/app/councils/client/views/index.js +++ b/app/councils/client/views/index.js @@ -46,12 +46,13 @@ export function CouncilsPage() { FlowRouter.go(`/council/${ _id }`); }; - const onEditClick = useCallback((_id) => () => { - router.push({ - context: 'edit', - id: _id, - }); - }, [router]); + const onEditClick = (_id) => () => { + FlowRouter.go(`/council/edit/${ _id }`); + }; + + const onAddClick = () => { + FlowRouter.go('/council/edit/new'); + }; const onHeaderClick = (id) => { const [sortBy, sortDirection] = sort; @@ -88,22 +89,22 @@ export function CouncilsPage() { - - + { context && - { context === 'edit' && t('Council_Info') } + {/*{ context === 'edit' && t('Council_Info') }*/} { context === 'new' && t('Council_Add') } - {context === 'edit' && } + {/*{context === 'edit' && }*/} {context === 'new' && } } ; diff --git a/app/councils/client/views/lib.js b/app/councils/client/views/lib.js index 98c0b28a2646..11775d0f827e 100644 --- a/app/councils/client/views/lib.js +++ b/app/councils/client/views/lib.js @@ -13,7 +13,7 @@ export function validate(councilData) { return errors; } -export function createCouncilData(date, description = '', previousData) { +export function createCouncilData(date, description = '', previousData, invitedUsers) { const councilData = { }; @@ -22,6 +22,7 @@ export function createCouncilData(date, description = '', previousData) { } councilData.d = date; councilData.desc = description; + councilData.invitedUsers = invitedUsers; return councilData; } diff --git a/app/councils/server/index.js b/app/councils/server/index.js index 1bd64f245eac..ede79335580e 100644 --- a/app/councils/server/index.js +++ b/app/councils/server/index.js @@ -1,4 +1,5 @@ import './methods/insertOrUpdateCouncil'; import './methods/deleteCouncil'; import './methods/addPersonToCouncil'; +import './methods/updatePersonCouncil'; import './methods/downloadCouncilParticipants'; diff --git a/app/councils/server/methods/updatePersonCouncil.js b/app/councils/server/methods/updatePersonCouncil.js new file mode 100644 index 000000000000..146b4a37a9fe --- /dev/null +++ b/app/councils/server/methods/updatePersonCouncil.js @@ -0,0 +1,47 @@ +import { Meteor } from 'meteor/meteor'; +import s from 'underscore.string'; + +import { Councils } from '../../../models'; + +Meteor.methods({ + updatePersonCouncil(id, person, index) { + if (!id) { + throw new Meteor.Error('error-the-field-is-required', 'The field id is required', { method: 'updatePersonCouncil', field: 'id' }); + } + + if (!person) { + throw new Meteor.Error('error-the-field-is-required', 'The field person is required', { method: 'updatePersonCouncil', field: 'person' }); + } + + + if (!s.trim(person.firstName)) { + throw new Meteor.Error('error-the-field-is-required', 'The field firstName is required', { method: 'updatePersonCouncil', field: 'firstName' }); + } + + if (!s.trim(person.lastName)) { + throw new Meteor.Error('error-the-field-is-required', 'The field lastName is required', { method: 'updatePersonCouncil', field: 'lastName' }); + } + + if (!s.trim(person.position)) { + throw new Meteor.Error('error-the-field-is-required', 'The field position is required', { method: 'updatePersonCouncil', field: 'position' }); + } + + if (!s.trim(person.phone)) { + throw new Meteor.Error('error-the-field-is-required', 'The field phone is required', { method: 'updatePersonCouncil', field: 'phone' }); + } + + if (!s.trim(person.email)) { + throw new Meteor.Error('error-the-field-is-required', 'The field email is required', { method: 'updatePersonCouncil', field: 'email' }); + } + + if (person.contactPersonFirstName !== undefined && !s.trim(person.contactPersonFirstName)) { + throw new Meteor.Error('error-the-field-is-required', 'The field contactPersonFirstName is required', { method: 'updatePersonCouncil', field: 'contactPersonFirstName' }); + } + + if (person.contactPersonLastName !== undefined && !s.trim(person.contactPersonLastName)) { + throw new Meteor.Error('error-the-field-is-required', 'The field contactPersonLastName is required', { method: 'updatePersonCouncil', field: 'contactPersonLastName' }); + } + + return Councils.updatePersonCouncil(id, person, index); + }, +}); diff --git a/app/discussion/client/views/creationDialog/CreateDiscussion.html b/app/discussion/client/views/creationDialog/CreateDiscussion.html index 4452789d1d78..f2f17b703a90 100644 --- a/app/discussion/client/views/creationDialog/CreateDiscussion.html +++ b/app/discussion/client/views/creationDialog/CreateDiscussion.html @@ -75,7 +75,7 @@
- +
diff --git a/app/lib/server/functions/saveUserIdentity.js b/app/lib/server/functions/saveUserIdentity.js index 699e50f62828..166272971a3f 100644 --- a/app/lib/server/functions/saveUserIdentity.js +++ b/app/lib/server/functions/saveUserIdentity.js @@ -11,7 +11,7 @@ import { updateGroupDMsName } from './updateGroupDMsName'; * @param {string} userId user performing the action * @param {object} changes changes to the user */ -export function saveUserIdentity(userId, { _id, name: rawName, username: rawUsername, surname: rawSurname, patronymic: rawPatronymc }) { +export function saveUserIdentity(userId, { _id, name: rawName, username: rawUsername, surname: rawSurname, patronymic: rawPatronymic }) { if (!_id) { return false; } @@ -19,7 +19,7 @@ export function saveUserIdentity(userId, { _id, name: rawName, username: rawUser const name = String(rawName).trim(); const username = String(rawUsername).trim(); const surname = String(rawSurname).trim(); - const patronymic = String(rawPatronymc).trim(); + const patronymic = String(rawPatronymic).trim(); const user = Users.findOneById(_id); @@ -51,11 +51,11 @@ export function saveUserIdentity(userId, { _id, name: rawName, username: rawUser } } - // if (typeof rawPatronymc !== 'undefined' && patronymicChanged) { - // if (!setPatronymic(_id, patronymic, user)) { - // return false; - // } - // } + if (typeof rawPatronymic !== 'undefined' && patronymicChanged) { + if (!setPatronymic(_id, patronymic, user)) { + return false; + } + } // if coming from old username, update all references if (previousUsername && usernameChanged) { diff --git a/app/models/server/models/Councils.js b/app/models/server/models/Councils.js index eef52f999db7..7417407aab1a 100644 --- a/app/models/server/models/Councils.js +++ b/app/models/server/models/Councils.js @@ -28,6 +28,14 @@ class Councils extends Base { return this.update({ _id }, { $set: { ...data } }); } + + updatePersonCouncil(_id, person, index) { + const data = this.findOne({ _id }); + data._updatedAt = new Date(); + data.invitedUsers[index] = person; + + return this.update({ _id }, { $set: { ...data } }); + } } export default new Councils(); diff --git a/app/reactions/client/init.js b/app/reactions/client/init.js index 371471b02d87..f7ad77804885 100644 --- a/app/reactions/client/init.js +++ b/app/reactions/client/init.js @@ -94,7 +94,7 @@ Meteor.startup(function() { return true; }, - order: 3, + order: 2, group: ['menu'], }); }); diff --git a/app/reactions/client/stylesheets/reaction.css b/app/reactions/client/stylesheets/reaction.css index a3fb880223a8..16c281e77c62 100644 --- a/app/reactions/client/stylesheets/reaction.css +++ b/app/reactions/client/stylesheets/reaction.css @@ -49,13 +49,13 @@ } &.add-reaction { - visibility: hidden; + visibility: visible; padding: 0 2px; transition: opacity 0.2s ease; - opacity: 0; + opacity: 0.5; color: #888888; } diff --git a/app/tags/client/stylesheets/tags.css b/app/tags/client/stylesheets/tags.css index fe4336864cdd..9053af27e22f 100644 --- a/app/tags/client/stylesheets/tags.css +++ b/app/tags/client/stylesheets/tags.css @@ -26,13 +26,13 @@ } &.add-tag { - visibility: hidden; + visibility: visible; padding: 0 2px; transition: opacity 0.2s ease; - opacity: 0; + opacity: 0.5; color: #888888; } diff --git a/app/theme/client/imports/components/goBackButton.css b/app/theme/client/imports/components/goBackButton.css index faa149e885b4..39936c13804d 100644 --- a/app/theme/client/imports/components/goBackButton.css +++ b/app/theme/client/imports/components/goBackButton.css @@ -1,5 +1,7 @@ .go-back-button { - border-color: transparent !important; + border-width: 0.12rem !important; + border-color: var(--rc-color-button-primary) !important; + border-radius: 10rem !important; background-color: transparent !important; font-size: 24px !important; diff --git a/app/theme/client/imports/components/messages.css b/app/theme/client/imports/components/messages.css index 78d8c6af9d64..67e515b97a60 100644 --- a/app/theme/client/imports/components/messages.css +++ b/app/theme/client/imports/components/messages.css @@ -215,14 +215,13 @@ text-align: center; text-decoration: none; color: var(--color-black) !important; - border: 1px solid var(--button-reply-primary-background); display: inline-block; border-radius: 0.6em; transition: all 0.5s ease-in-out; position: relative; overflow: hidden; &:hover { - border-color: #4fb0fc; + border: 1px solid #4fb0fc; color: var(--color-black) !important; opacity: 1 !important; } diff --git a/app/theme/client/imports/general/base_old.css b/app/theme/client/imports/general/base_old.css index f7c1911ead14..1988de3d7c03 100755 --- a/app/theme/client/imports/general/base_old.css +++ b/app/theme/client/imports/general/base_old.css @@ -2130,6 +2130,7 @@ padding: 8px 50px 4px 70px; line-height: 20px; + margin-block-end: 15px; &-unread { display: inline-block; diff --git a/app/ui-master/public/icons.svg b/app/ui-master/public/icons.svg index bfd043b673e6..77c4309e8e97 100644 --- a/app/ui-master/public/icons.svg +++ b/app/ui-master/public/icons.svg @@ -366,6 +366,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/ui-master/public/icons/plus_in_circle.svg b/app/ui-master/public/icons/plus_in_circle.svg new file mode 100644 index 000000000000..3179ae4a65b3 --- /dev/null +++ b/app/ui-master/public/icons/plus_in_circle.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/ui-message/client/message.js b/app/ui-message/client/message.js index c579c0a8027e..005eed124f61 100755 --- a/app/ui-message/client/message.js +++ b/app/ui-message/client/message.js @@ -334,7 +334,7 @@ Template.message.helpers({ hideReactions() { const { msg } = this; if (_.isEmpty(msg.reactions)) { - return 'hidden'; + //return 'hidden'; } }, tags() { @@ -370,7 +370,7 @@ Template.message.helpers({ hideTags() { const { msg } = this; if (_.isEmpty(msg.tags)) { - return 'hidden'; + //return 'hidden'; } }, hideAddReaction() { diff --git a/app/ui-sidenav/client/sidebarHeader.html b/app/ui-sidenav/client/sidebarHeader.html index b634c16fc31f..c1d244cf70b7 100644 --- a/app/ui-sidenav/client/sidebarHeader.html +++ b/app/ui-sidenav/client/sidebarHeader.html @@ -7,7 +7,7 @@
- +
diff --git a/app/working-group-meetings/client/views/workingGroupMeeting.js b/app/working-group-meetings/client/views/workingGroupMeeting.js index 881744823bc7..07014050aad3 100644 --- a/app/working-group-meetings/client/views/workingGroupMeeting.js +++ b/app/working-group-meetings/client/views/workingGroupMeeting.js @@ -127,7 +127,6 @@ export function WorkingGroupMeetingPage() { console.log('!fileupload_enabled'); return null; } - console.log('fileuploadenabled'); const $input = $(document.createElement('input')); $input.css('display', 'none'); $input.attr({ diff --git a/app/working-group/client/views/index.js b/app/working-group/client/views/index.js index aac8b0f544ad..187e625a68c0 100644 --- a/app/working-group/client/views/index.js +++ b/app/working-group/client/views/index.js @@ -82,7 +82,7 @@ export function WorkingGroupPage() { const onHeaderClick = (id) => { const [sortBy, sortDirection] = sort; if (sortBy === id) { - setSort([id, sortDirection === 'asc' ? 'workingGroupType' : 'asc']); + setSort([id, sortDirection === 'asc' ? 'workingGroup' : 'asc']); return; } setSort([id, 'asc']); diff --git a/client/components/GenericTable.js b/client/components/GenericTable.js index ed6824d5ca69..95cd5af9bd19 100644 --- a/client/components/GenericTable.js +++ b/client/components/GenericTable.js @@ -6,7 +6,7 @@ import flattenChildren from 'react-keyed-flatten-children'; import { useTranslation } from '../contexts/TranslationContext'; function SortIcon({ direction }) { - return + return ; diff --git a/client/components/basic/UserInfo.js b/client/components/basic/UserInfo.js index 8f846e97b6ef..5769dcc75332 100644 --- a/client/components/basic/UserInfo.js +++ b/client/components/basic/UserInfo.js @@ -54,8 +54,15 @@ export const UserInfo = React.memo(function UserInfo({ {actions} - - {customStatus} + {shortFio && <> + + {shortFio} + } + {username && <> + + + { customStatus } + } {!!roles && <> @@ -67,19 +74,9 @@ export const UserInfo = React.memo(function UserInfo({ } - {username && username !== name && <> - - {username} - } - {lastLogin ? timeAgo(lastLogin) : t('Never')} - {shortFio && <> - - {shortFio} - } - {nickname && <> {nickname} diff --git a/client/routes.js b/client/routes.js index a7e46114a96e..146e1a251cbb 100755 --- a/client/routes.js +++ b/client/routes.js @@ -120,6 +120,16 @@ FlowRouter.route('/council/:id', { }], }); +FlowRouter.route('/council/edit/:context?/:id?', { + name: 'council-edit', + action: () => { + renderRouteComponent(() => import('../app/councils/client/views/EditCouncil'), { template: 'main', region: 'center' }); + }, + triggersExit: [function() { + $('.main-content').addClass('rc-old'); + }], +}); + FlowRouter.route('/councils/:context?/:id?', { name: 'councils', action: () => { diff --git a/packages/rocketchat-i18n/i18n/ru.i18n.json b/packages/rocketchat-i18n/i18n/ru.i18n.json index 27f5296486f2..6244a9db67a0 100755 --- a/packages/rocketchat-i18n/i18n/ru.i18n.json +++ b/packages/rocketchat-i18n/i18n/ru.i18n.json @@ -741,15 +741,20 @@ "could-not-access-webdav": "Не удалось получить доступ к WebDAV", "Council": "Мероприятие", "Council_Add": "Добавить мероприятие", + "Council_Add_Participant": "Добавить участника", "Council_info": "Информация о мероприятии", "Council_info_description": "Вы приглашены на мероприятие. Ознакомьтесь с его деталями.", "Council_date": "Дата мероприятия", + "Council_edit": "Редактирование мероприятия", + "Council_edited": "Мероприятие отредактировано", "Council_from": "Мероприятия от", "Council_invite_error": "Приглашения на данное мероприятие не существует", "Council_Added_Successfully": "Мероприятие успешно добавлено", "Council_Delete_Warning": "Удаление мероприятия нельзя отменить", + "Council_Delete_Participant_Warning": "Удалить участника?", "Council_Error_Invalid_Council": "Некорректное мероприятие", "Council_Has_Been_Deleted": "Мероприятие успешно удалено", + "Council_Participant_Has_Been_Deleted": "Участник успешно удален", "Council_Info": "Информация о мероприятии", "Council_Invited_Users": "Участники", "Council_Invited_Users_List": "Список участников мероприятия", @@ -1269,6 +1274,7 @@ "Edit_Trigger": "Изменить триггер", "Edit_User": "Редактировать пользователя", "edited": "отредактировано", + "Editing": "Редактирование", "Editing_room": "Редактирование комнаты", "Editing_user": "Редактирование пользователя", "Education": "Образование", diff --git a/private/public/icons.svg b/private/public/icons.svg index bfd043b673e6..77c4309e8e97 100644 --- a/private/public/icons.svg +++ b/private/public/icons.svg @@ -366,6 +366,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +