mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
Added User Notes
This commit is contained in:
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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';
|
||||
@@ -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 || []}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 || []}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -49,6 +49,10 @@ var userSchema = new Schema({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
adminNotes: [{
|
||||
note: { type: String },
|
||||
createdAt: { type: Date }
|
||||
}],
|
||||
|
||||
deleted: { type: Boolean, default: false},
|
||||
|
||||
|
||||
@@ -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 };
|
||||
},
|
||||
|
||||
@@ -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]){
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user