Added User Notes

This commit is contained in:
ioedeveloper
2019-09-05 07:46:38 +00:00
parent cadf9a1c97
commit 693b9e6f94
11 changed files with 214 additions and 22 deletions

View File

@@ -1,4 +1,4 @@
import { getApi, putApi, deleteApi } from '../api';
import { getApi, putApi, deleteApi, postApi } from '../api';
import * as types from '../constants/user';
import errors from '../errors'
@@ -353,3 +353,56 @@ export function unblockUser(userId) {
return promise;
};
}
//Add Project Notes
export function addUserNoteRequest() {
return {
type: types.ADD_USER_NOTE_REQUEST,
};
}
export function addUserNoteReset() {
return {
type: types.ADD_USER_NOTE_RESET,
};
}
export function addUserNoteSuccess(userNote) {
return {
type: types.ADD_USER_NOTE_SUCCESS,
payload: userNote
};
}
export function addUserNoteError(error) {
return {
type: types.ADD_USER_NOTE_FAILURE,
payload: error
};
}
// Calls the API to add Admin Note
export function addUserNote(userId, values) {
return function (dispatch) {
var promise;
promise = postApi(`user/${userId}/addNote`, values);
dispatch(addUserNoteRequest());
promise.then(function (response) {
const data = response.data;
dispatch(addUserNoteSuccess(data));
}, function (error) {
if (error && error.response && error.response.data)
error = error.response.data;
if (error && error.data) {
error = error.data;
}
if (error && error.message) {
error = error.message;
}else{
error = 'Network Error';
}
dispatch(addUserNoteError(errors(error)));
});
return promise;
};
}

View File

@@ -40,9 +40,7 @@ export class AdminNotes extends Component {
}
submitForm = (values) => {
if(this.props.projectId){
this.props.addNote(this.props.projectId, values.adminNotes);
}
this.props.addNote(this.props.id, values.adminNotes);
if (window.location.href.indexOf('localhost') <= -1) {
this.context.mixpanel.track('Admin Notes Updated', values);
}
@@ -59,7 +57,7 @@ export class AdminNotes extends Component {
<span className="Text-color--inherit Text-display--inline Text-fontSize--16 Text-fontWeight--medium Text-lineHeight--24 Text-typeface--base Text-wrap--wrap">
<span>Admin Notes</span>
</span>
<p><span>Leave a comment for this user.</span></p>
<p><span>Leave a comment.</span></p>
</div>
</div>
<form onSubmit={handleSubmit(this.submitForm)}>
@@ -107,18 +105,14 @@ const mapDispatchToProps = dispatch => bindActionCreators(
{ }
, dispatch);
const mapStateToProps = (state, props) => {
const projectId = props.projectId;
const project = state.project.projects.projects.find(project => project._id === projectId) || {}
return {
initialValues: { adminNotes: project.adminNotes || []}
}
const mapStateToProps = state => {
return {};
}
AdminNotes.propTypes = {
requesting: PropTypes.bool,
addNote: PropTypes.func.isRequired,
projectId: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
}
let AdminNotesForm = reduxForm({

View File

@@ -36,4 +36,10 @@ export const BLOCK_USER_FAILED = 'BLOCK_USER_FAILED';
export const UNBLOCK_USER_REQUEST = 'UNBLOCK_USER_REQUEST';
export const UNBLOCK_USER_RESET = 'UNBLOCK_USER_RESET';
export const UNBLOCK_USER_SUCCESS = 'UNBLOCK_USER_SUCCESS';
export const UNBLOCK_USER_FAILED = 'UNBLOCK_USER_FAILED';
export const UNBLOCK_USER_FAILED = 'UNBLOCK_USER_FAILED';
// Admin User Note
export const ADD_USER_NOTE_REQUEST = 'ADD_USER_NOTE_REQUEST';
export const ADD_USER_NOTE_RESET = 'ADD_USER_NOTE_RESET';
export const ADD_USER_NOTE_SUCCESS = 'ADD_USER_NOTE_SUCCESS';
export const ADD_USER_NOTE_FAILURE = 'ADD_USER_NOTE_FAILURE';

View File

@@ -41,7 +41,7 @@ class Project extends Component {
<ProjectDetails projectId={this.props.project ? this.props.project._id : ''} />
</div>
<div className="Box-root Margin-bottom--12">
<AdminNotes projectId={this.props.project ? this.props.project._id : ''} addNote={this.props.addProjectNote} />
<AdminNotes id={this.props.project ? this.props.project._id : ''} addNote={this.props.addProjectNote} initialValues={this.props.initialValues} />
</div>
<ShouldRender if={this.props.project && !this.props.project.deleted && !this.props.project.isBlocked}>
<div className="Box-root Margin-bottom--12">
@@ -84,6 +84,7 @@ const mapStateToProps = (state, props) => {
return {
project,
adminNote: state.adminNote,
initialValues: { adminNotes: project.adminNotes || []}
}
}

View File

@@ -10,7 +10,9 @@ import UserDeleteBox from '../components/user/UserDeleteBox';
import UserRestoreBox from '../components/user/UserRestoreBox';
import UserBlockBox from '../components/user/UserBlockBox';
import UserUnblockBox from '../components/user/UserUnblockBox';
import AdminNotes from '../components/adminNote/AdminNotes';
import { fetchUserProjects } from '../actions/project';
import { addUserNote } from '../actions/user';
class User extends Component {
@@ -43,6 +45,9 @@ class User extends Component {
<div className="Box-root Margin-bottom--12">
<UserProject userId={this.props.match.params.userId} />
</div>
<div className="Box-root Margin-bottom--12">
<AdminNotes id={this.props.match.params.userId} addNote={this.props.addUserNote} initialValues={this.props.initialValues} />
</div>
<ShouldRender if={this.props.user && !this.props.user.deleted && !this.props.user.isBlocked} >
<div className="Box-root Margin-bottom--12">
<UserBlockBox userId={this.props.match.params.userId} />
@@ -77,13 +82,14 @@ class User extends Component {
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({ fetchUserProjects }, dispatch)
return bindActionCreators({ fetchUserProjects, addUserNote }, dispatch)
}
const mapStateToProps = (state, props) => {
const user = state.user.users.users.find(user => user._id === props.match.params.userId);
const user = state.user.users.users.find(user => user._id === props.match.params.userId) || {};
return {
user
user,
initialValues: { adminNotes: user.adminNotes || []}
}
}

View File

@@ -28,6 +28,11 @@ import {
UNBLOCK_USER_REQUEST,
UNBLOCK_USER_RESET,
UNBLOCK_USER_SUCCESS,
ADD_USER_NOTE_REQUEST,
ADD_USER_NOTE_RESET,
ADD_USER_NOTE_SUCCESS,
ADD_USER_NOTE_FAILURE,
} from '../constants/user';
const INITIAL_STATE = {
@@ -65,7 +70,12 @@ const INITIAL_STATE = {
error: null,
requesting: false,
success: false
}
},
newUserNote: {
requesting: false,
error: null,
success: false,
},
};
export default function user(state = INITIAL_STATE, action) {
@@ -340,6 +350,54 @@ export default function user(state = INITIAL_STATE, action) {
error: null,
}
});
// add user admin notes
case ADD_USER_NOTE_REQUEST:
return Object.assign({}, state, {
newUserNote: {
requesting: true,
error: null,
success: false,
},
});
case ADD_USER_NOTE_SUCCESS:
return Object.assign({}, state, {
users: {
requesting: false,
error: null,
success: true,
users: state.users.users.map(user=> {
user.adminNotes = user._id === action.payload.projectId ? action.payload.notes : user.adminNotes;
return user;
}),
count: state.users.count,
limit: state.users.limit,
skip: state.users.skip
},
newUserNote:{
requesting: false,
error: null,
success: true
}
});
case ADD_USER_NOTE_FAILURE:
return Object.assign({}, state, {
newUserNote: {
requesting: false,
error: action.payload,
success: false,
},
});
case ADD_USER_NOTE_RESET:
return Object.assign({}, state, {
...INITIAL_STATE
});
default: return state;
}

View File

@@ -694,4 +694,52 @@ router.put('/:userId/unblockUser', getUser, isUserMasterAdmin, async function (r
}
});
router.post('/:userId/addNote', getUser, isUserMasterAdmin, async function (req, res){
const userId = req.params.userId;
if(Array.isArray(req.body)){
let data = [];
if(req.body.length > 0){
for(let val of req.body){
if(!val._id){
// Sanitize
if (!val.note) {
return sendErrorResponse( req, res, {
code: 400,
message: 'User note must be present.'
});
}
if (typeof val.note !== 'string') {
return sendErrorResponse( req, res, {
code: 400,
message: 'User note is not in string format.'
});
}
}
data.push(val);
}
try{
let adminNotes = await UserService.addNotes(userId, data);
return sendItemResponse(req, res, adminNotes);
}catch(error){
return sendErrorResponse(req, res, error);
}
}else{
try{
let adminNotes = await UserService.addNotes(userId, data);
return sendItemResponse(req, res, adminNotes);
}catch(error){
return sendErrorResponse(req, res, error);
}
}
}else{
return sendErrorResponse( req, res, {
code: 400,
message: 'Admin notes are expected in array format.'
});
}
});
module.exports = router;

View File

@@ -49,6 +49,10 @@ var userSchema = new Schema({
type: Boolean,
default: false
},
adminNotes: [{
note: { type: String },
createdAt: { type: Date }
}],
deleted: { type: Boolean, default: false},

View File

@@ -525,9 +525,17 @@ module.exports = {
// add project monitors
projects = await Promise.all(projects.map(async(project) => {
// get both sub-project users and project users
const users = await TeamService.getTeamMembersBy({parentProjectId: project._id});
project.users = users;
return project;
let users = [];
if(project.parentProjectId){
users = await TeamService.getTeamMembersBy({parentProjectId: project.parentProjectId});
project.users = users;
}else{
users = await Promise.all(project.users.map(async (user) => {
return await UserService.findOneBy({ _id: user.userId });
}));
project.users = users;
}
return Object.assign({}, project._doc, { users });
}));
return { projects, count };
},

View File

@@ -38,6 +38,9 @@ module.exports = {
users.push(user);
}
// eslint-disable-next-line no-console
console.log('users: ', users);
var response = [];
for (let i = 0; i < users.length; i++) {
if(users[i]){

View File

@@ -44,6 +44,7 @@ module.exports = {
userModel.timezone = data.timezone || null;
userModel.lastActive = data.lastActive || Date.now();
userModel.coupon = data.coupon || null;
userModel.adminNotes = data.adminNotes || null;
try{
var user = await userModel.save();
}catch(error){
@@ -144,6 +145,7 @@ module.exports = {
var lastActive = data.lastActive || user.lastActive;
var coupon = data.coupon || user.coupon;
var disabled = data.disabled || false;
var adminNotes = data.adminNotes || user.adminNotes;
var isBlocked = user.isBlocked;
if(typeof data.isBlocked === 'boolean'){
@@ -184,7 +186,8 @@ module.exports = {
deleted,
deletedById,
deletedAt,
isBlocked
isBlocked,
adminNotes
}
}, {
new: true
@@ -648,6 +651,14 @@ module.exports = {
return user;
}
},
addNotes: async function(userId, notes){
const _this = this;
let adminNotes = (await _this.update({
_id: userId,
adminNotes: notes
})).adminNotes;
return adminNotes;
},
hardDeleteBy: async function(query){
try{
await UserModel.deleteMany(query);