mirror of
https://github.com/plankanban/planka.git
synced 2025-07-19 05:09:43 +02:00
Add preferences tab to user settings, add subscribe to own cards option
This commit is contained in:
parent
f3e0cadca6
commit
6a82448dc7
13 changed files with 96 additions and 2 deletions
34
client/src/components/UserSettingsModal/PreferencesPane.jsx
Normal file
34
client/src/components/UserSettingsModal/PreferencesPane.jsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Radio, Tab } from 'semantic-ui-react';
|
||||||
|
|
||||||
|
import styles from './PreferencesPane.module.css';
|
||||||
|
|
||||||
|
const PreferencesPane = React.memo(({ subscribeToOwnCards, onUpdate }) => {
|
||||||
|
const [t] = useTranslation();
|
||||||
|
|
||||||
|
const handleSubscribeToOwnCardsChange = useCallback(() => {
|
||||||
|
onUpdate({
|
||||||
|
subscribeToOwnCards: !subscribeToOwnCards,
|
||||||
|
});
|
||||||
|
}, [subscribeToOwnCards, onUpdate]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tab.Pane attached={false} className={styles.wrapper}>
|
||||||
|
<Radio
|
||||||
|
toggle
|
||||||
|
checked={subscribeToOwnCards}
|
||||||
|
label={t('common.subscribeToMyOwnCardsByDefault')}
|
||||||
|
onChange={handleSubscribeToOwnCardsChange}
|
||||||
|
/>
|
||||||
|
</Tab.Pane>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
PreferencesPane.propTypes = {
|
||||||
|
subscribeToOwnCards: PropTypes.bool.isRequired,
|
||||||
|
onUpdate: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PreferencesPane;
|
|
@ -0,0 +1,4 @@
|
||||||
|
.wrapper {
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||||
import { Modal, Tab } from 'semantic-ui-react';
|
import { Modal, Tab } from 'semantic-ui-react';
|
||||||
|
|
||||||
import AccountPane from './AccountPane';
|
import AccountPane from './AccountPane';
|
||||||
|
import PreferencesPane from './PreferencesPane';
|
||||||
|
|
||||||
const UserSettingsModal = React.memo(
|
const UserSettingsModal = React.memo(
|
||||||
({
|
({
|
||||||
|
@ -13,6 +14,7 @@ const UserSettingsModal = React.memo(
|
||||||
avatar,
|
avatar,
|
||||||
phone,
|
phone,
|
||||||
organization,
|
organization,
|
||||||
|
subscribeToOwnCards,
|
||||||
isAvatarUploading,
|
isAvatarUploading,
|
||||||
usernameUpdateForm,
|
usernameUpdateForm,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
|
@ -57,12 +59,26 @@ const UserSettingsModal = React.memo(
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
menuItem: t('common.preferences', {
|
||||||
|
context: 'title',
|
||||||
|
}),
|
||||||
|
render: () => (
|
||||||
|
<PreferencesPane subscribeToOwnCards={subscribeToOwnCards} onUpdate={onUpdate} />
|
||||||
|
),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal open closeIcon size="small" centered={false} onClose={onClose}>
|
<Modal open closeIcon size="small" centered={false} onClose={onClose}>
|
||||||
<Modal.Content>
|
<Modal.Content>
|
||||||
<Tab menu={{ secondary: true, pointing: true }} panes={panes} />
|
<Tab
|
||||||
|
menu={{
|
||||||
|
secondary: true,
|
||||||
|
pointing: true,
|
||||||
|
}}
|
||||||
|
panes={panes}
|
||||||
|
/>
|
||||||
</Modal.Content>
|
</Modal.Content>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
@ -76,6 +92,7 @@ UserSettingsModal.propTypes = {
|
||||||
avatar: PropTypes.string,
|
avatar: PropTypes.string,
|
||||||
phone: PropTypes.string,
|
phone: PropTypes.string,
|
||||||
organization: PropTypes.string,
|
organization: PropTypes.string,
|
||||||
|
subscribeToOwnCards: PropTypes.bool.isRequired,
|
||||||
isAvatarUploading: PropTypes.bool.isRequired,
|
isAvatarUploading: PropTypes.bool.isRequired,
|
||||||
/* eslint-disable react/forbid-prop-types */
|
/* eslint-disable react/forbid-prop-types */
|
||||||
usernameUpdateForm: PropTypes.object.isRequired,
|
usernameUpdateForm: PropTypes.object.isRequired,
|
||||||
|
|
|
@ -23,6 +23,7 @@ const mapStateToProps = (state) => {
|
||||||
avatar,
|
avatar,
|
||||||
phone,
|
phone,
|
||||||
organization,
|
organization,
|
||||||
|
subscribeToOwnCards,
|
||||||
isAvatarUploading,
|
isAvatarUploading,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
passwordUpdateForm,
|
passwordUpdateForm,
|
||||||
|
@ -36,6 +37,7 @@ const mapStateToProps = (state) => {
|
||||||
avatar,
|
avatar,
|
||||||
phone,
|
phone,
|
||||||
organization,
|
organization,
|
||||||
|
subscribeToOwnCards,
|
||||||
isAvatarUploading,
|
isAvatarUploading,
|
||||||
emailUpdateForm,
|
emailUpdateForm,
|
||||||
passwordUpdateForm,
|
passwordUpdateForm,
|
||||||
|
|
|
@ -86,12 +86,14 @@ export default {
|
||||||
optional_inline: 'optional',
|
optional_inline: 'optional',
|
||||||
organization: 'Organization',
|
organization: 'Organization',
|
||||||
phone: 'Phone',
|
phone: 'Phone',
|
||||||
|
preferences: 'Preferences',
|
||||||
projectNotFound_title: 'Project Not Found',
|
projectNotFound_title: 'Project Not Found',
|
||||||
refreshPageToLoadLastDataAndReceiveUpdates:
|
refreshPageToLoadLastDataAndReceiveUpdates:
|
||||||
'<0>Refresh the page</0> to load last data<br />and receive updates',
|
'<0>Refresh the page</0> to load last data<br />and receive updates',
|
||||||
removeMember_title: 'Remove Member',
|
removeMember_title: 'Remove Member',
|
||||||
seconds: 'Seconds',
|
seconds: 'Seconds',
|
||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
|
subscribeToMyOwnCardsByDefault: 'Subscribe to my own cards by default',
|
||||||
taskActions_title: 'Task Actions',
|
taskActions_title: 'Task Actions',
|
||||||
tasks: 'Tasks',
|
tasks: 'Tasks',
|
||||||
time: 'Time',
|
time: 'Time',
|
||||||
|
|
|
@ -90,12 +90,14 @@ export default {
|
||||||
optional_inline: 'необязательно',
|
optional_inline: 'необязательно',
|
||||||
organization: 'Организация',
|
organization: 'Организация',
|
||||||
phone: 'Телефон',
|
phone: 'Телефон',
|
||||||
|
preferences: 'Предпочтения',
|
||||||
projectNotFound: 'Доска не найдена',
|
projectNotFound: 'Доска не найдена',
|
||||||
refreshPageToLoadLastDataAndReceiveUpdates:
|
refreshPageToLoadLastDataAndReceiveUpdates:
|
||||||
'<0>Обновите страницу</0>, чтобы загрузить<br />актуальные данные и получать обновления',
|
'<0>Обновите страницу</0>, чтобы загрузить<br />актуальные данные и получать обновления',
|
||||||
removeMember: 'Удаление участника',
|
removeMember: 'Удаление участника',
|
||||||
seconds: 'Секунды',
|
seconds: 'Секунды',
|
||||||
settings: 'Настройки',
|
settings: 'Настройки',
|
||||||
|
subscribeToMyOwnCardsByDefault: 'По умолчанию подписаться на мои собственные карточки',
|
||||||
taskActions: 'Действия с задачей',
|
taskActions: 'Действия с задачей',
|
||||||
tasks: 'Задачи',
|
tasks: 'Задачи',
|
||||||
time: 'Время',
|
time: 'Время',
|
||||||
|
|
|
@ -39,6 +39,7 @@ export default class extends Model {
|
||||||
avatar: attr(),
|
avatar: attr(),
|
||||||
phone: attr(),
|
phone: attr(),
|
||||||
organization: attr(),
|
organization: attr(),
|
||||||
|
subscribeToOwnCards: attr(),
|
||||||
deletedAt: attr(),
|
deletedAt: attr(),
|
||||||
isAdmin: attr({
|
isAdmin: attr({
|
||||||
getDefault: () => false,
|
getDefault: () => false,
|
||||||
|
|
|
@ -40,6 +40,9 @@ module.exports = {
|
||||||
isNotEmptyString: true,
|
isNotEmptyString: true,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
|
subscribeToOwnCards: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
exits: {
|
exits: {
|
||||||
|
@ -59,6 +62,7 @@ module.exports = {
|
||||||
'username',
|
'username',
|
||||||
'phone',
|
'phone',
|
||||||
'organization',
|
'organization',
|
||||||
|
'subscribeToOwnCards',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const user = await sails.helpers
|
const user = await sails.helpers
|
||||||
|
|
|
@ -32,6 +32,9 @@ module.exports = {
|
||||||
isNotEmptyString: true,
|
isNotEmptyString: true,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
|
subscribeToOwnCards: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
exits: {
|
exits: {
|
||||||
|
@ -57,7 +60,14 @@ module.exports = {
|
||||||
throw Errors.USER_NOT_FOUND;
|
throw Errors.USER_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
const values = _.pick(inputs, ['isAdmin', 'name', 'avatar', 'phone', 'organization']);
|
const values = _.pick(inputs, [
|
||||||
|
'isAdmin',
|
||||||
|
'name',
|
||||||
|
'avatar',
|
||||||
|
'phone',
|
||||||
|
'organization',
|
||||||
|
'subscribeToOwnCards',
|
||||||
|
]);
|
||||||
|
|
||||||
user = await sails.helpers.updateUser(user, values, this.req);
|
user = await sails.helpers.updateUser(user, values, this.req);
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,17 @@ module.exports = {
|
||||||
boardId: inputs.list.boardId,
|
boardId: inputs.list.boardId,
|
||||||
}).fetch();
|
}).fetch();
|
||||||
|
|
||||||
|
if (inputs.user.subscribeToOwnCards) {
|
||||||
|
await CardSubscription.create({
|
||||||
|
cardId: card.id,
|
||||||
|
userId: inputs.user.id,
|
||||||
|
}).tolerate('E_UNIQUE');
|
||||||
|
|
||||||
|
card.isSubscribed = true;
|
||||||
|
} else {
|
||||||
|
card.isSubscribed = false;
|
||||||
|
}
|
||||||
|
|
||||||
sails.sockets.broadcast(
|
sails.sockets.broadcast(
|
||||||
`board:${card.boardId}`,
|
`board:${card.boardId}`,
|
||||||
'cardCreate',
|
'cardCreate',
|
||||||
|
|
|
@ -52,6 +52,11 @@ module.exports = {
|
||||||
isNotEmptyString: true,
|
isNotEmptyString: true,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
},
|
},
|
||||||
|
subscribeToOwnCards: {
|
||||||
|
type: 'boolean',
|
||||||
|
defaultsTo: false,
|
||||||
|
columnName: 'subscribe_to_own_cards',
|
||||||
|
},
|
||||||
deletedAt: {
|
deletedAt: {
|
||||||
type: 'ref',
|
type: 'ref',
|
||||||
columnName: 'deleted_at',
|
columnName: 'deleted_at',
|
||||||
|
|
|
@ -13,6 +13,7 @@ module.exports.up = (knex) =>
|
||||||
table.text('avatar');
|
table.text('avatar');
|
||||||
table.text('phone');
|
table.text('phone');
|
||||||
table.text('organization');
|
table.text('organization');
|
||||||
|
table.boolean('subscribe_to_own_cards').notNullable();
|
||||||
|
|
||||||
table.timestamp('created_at', true);
|
table.timestamp('created_at', true);
|
||||||
table.timestamp('updated_at', true);
|
table.timestamp('updated_at', true);
|
||||||
|
|
|
@ -9,6 +9,7 @@ exports.seed = (knex) => {
|
||||||
isAdmin: true,
|
isAdmin: true,
|
||||||
name: 'Demo Demo',
|
name: 'Demo Demo',
|
||||||
username: 'demo',
|
username: 'demo',
|
||||||
|
subscribeToOwnCards: false,
|
||||||
createdAt: date,
|
createdAt: date,
|
||||||
updatedAt: date,
|
updatedAt: date,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue