kpfadnis commited on
Commit
b6e592e
·
1 Parent(s): 4594d15

feat (comments): Allow user to delete and edit comment.

Browse files

Signed-off-by: Kshitij Fadnis <[email protected]>

VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
 
1
+ 1.0.2
src/components/comments/CommentsViewer.tsx CHANGED
@@ -23,9 +23,22 @@ import { Button, Tag } from '@carbon/react';
23
  import { Edit, TrashCan } from '@carbon/icons-react';
24
 
25
  import { TaskComment, Model } from '@/src/types';
 
26
 
27
  import classes from './CommentViewer.module.scss';
28
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  function Comment({
30
  id,
31
  comment,
@@ -40,8 +53,6 @@ function Comment({
40
  models: Map<string, Model> | undefined;
41
  }) {
42
  const [editing, setEditing] = useState<boolean>(false);
43
- const [editedCommentText, setEditedCommentText] = useState<string>('');
44
- const [author, setAuthor] = useState<string>('');
45
 
46
  const [tag, tagType]: [string, string] = useMemo(() => {
47
  if (comment.provenance) {
@@ -63,66 +74,73 @@ function Comment({
63
  }, [comment.provenance, models]);
64
 
65
  return (
66
- <div className={classes.comment}>
67
- <div className={classes.commentHeader}>
68
- <div className={classes.commentHeaderAuthor}>
69
- <span className={classes.label}>Author</span>
70
- <span>{comment.author}</span>
71
- </div>
72
- <div className={classes.commentHeaderProvenance}>
73
- <span className={classes.label}>Provenance</span>
74
- <Tag className={classes.commentTag} type={tagType}>
75
- {tag}
76
- </Tag>
77
- </div>
78
 
79
- <span className={classes.commentHeaderTimestamp}>
80
- <span className={classes.label}>Last updated</span>
81
- <span>{new Date(comment.updated).toLocaleDateString()}</span>
82
- </span>
83
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
- <div className={classes.commentBody}>{comment.comment}</div>
86
- <div className={classes.commentActions}>
87
- <Button
88
- id={`${id}-editBtn`}
89
- className={classes.commentBtn}
90
- kind={'ghost'}
91
- onClick={() => {
92
- setEditing(true);
93
- }}
94
- disabled={true}
95
- >
96
- <span>Edit</span>
97
- <Edit />
98
- </Button>
99
- <Button
100
- id={`${id}-deleteBtn`}
101
- className={classes.commentBtn}
102
- kind={'ghost'}
103
- onClick={onDelete}
104
- disabled={true}
105
- >
106
- <span>Delete</span>
107
- <TrashCan />
108
- </Button>
109
  </div>
110
- </div>
111
  );
112
  }
113
 
114
- interface Props {
115
- comments: TaskComment[];
116
- onSelect: Function;
117
- onEdit: Function;
118
- onDelete: Function;
119
- models: Map<string, Model> | undefined;
120
- }
121
  export default memo(function ViewComments({
122
  comments,
123
- onSelect,
124
- onEdit,
125
- onDelete,
126
  models,
127
  }: Props) {
128
  return (
@@ -134,8 +152,10 @@ export default memo(function ViewComments({
134
  key={`comment-${commentIdx}`}
135
  id={`comment-${commentIdx}`}
136
  comment={comment}
137
- onEdit={onEdit}
138
- onDelete={onDelete}
 
 
139
  models={models}
140
  />
141
  );
 
23
  import { Edit, TrashCan } from '@carbon/icons-react';
24
 
25
  import { TaskComment, Model } from '@/src/types';
26
+ import EditCommentModal from '@/src/components/comments/EditCommentModal';
27
 
28
  import classes from './CommentViewer.module.scss';
29
 
30
+ // ===================================================================================
31
+ // TYPES
32
+ // ===================================================================================
33
+ interface Props {
34
+ comments: TaskComment[];
35
+ onUpdate: Function;
36
+ models: Map<string, Model> | undefined;
37
+ }
38
+
39
+ // ===================================================================================
40
+ // RENDER FUNCTION
41
+ // ===================================================================================
42
  function Comment({
43
  id,
44
  comment,
 
53
  models: Map<string, Model> | undefined;
54
  }) {
55
  const [editing, setEditing] = useState<boolean>(false);
 
 
56
 
57
  const [tag, tagType]: [string, string] = useMemo(() => {
58
  if (comment.provenance) {
 
74
  }, [comment.provenance, models]);
75
 
76
  return (
77
+ <>
78
+ <EditCommentModal
79
+ comment={comment}
80
+ open={editing}
81
+ onClose={() => setEditing(false)}
82
+ onSubmit={(updatedComment) => {
83
+ // Step 1: Update comment
84
+ onEdit(updatedComment);
 
 
 
 
85
 
86
+ // Step 2: Close editing modal
87
+ setEditing(false);
88
+ }}
89
+ models={models}
90
+ />
91
+ <div className={classes.comment}>
92
+ <div className={classes.commentHeader}>
93
+ <div className={classes.commentHeaderAuthor}>
94
+ <span className={classes.label}>Author</span>
95
+ <span>{comment.author}</span>
96
+ </div>
97
+ <div className={classes.commentHeaderProvenance}>
98
+ <span className={classes.label}>Provenance</span>
99
+ <Tag className={classes.commentTag} type={tagType}>
100
+ {tag}
101
+ </Tag>
102
+ </div>
103
+
104
+ <span className={classes.commentHeaderTimestamp}>
105
+ <span className={classes.label}>Last updated</span>
106
+ <span>{new Date(comment.updated).toLocaleDateString()}</span>
107
+ </span>
108
+ </div>
109
 
110
+ <div className={classes.commentBody}>{comment.comment}</div>
111
+ <div className={classes.commentActions}>
112
+ <Button
113
+ id={`${id}-editBtn`}
114
+ className={classes.commentBtn}
115
+ kind={'ghost'}
116
+ onClick={() => {
117
+ setEditing(true);
118
+ }}
119
+ >
120
+ <span>Edit</span>
121
+ <Edit />
122
+ </Button>
123
+ <Button
124
+ id={`${id}-deleteBtn`}
125
+ className={classes.commentBtn}
126
+ kind={'ghost'}
127
+ onClick={onDelete}
128
+ >
129
+ <span>Delete</span>
130
+ <TrashCan />
131
+ </Button>
132
+ </div>
 
133
  </div>
134
+ </>
135
  );
136
  }
137
 
138
+ // ===================================================================================
139
+ // MAIN FUNCTION
140
+ // ===================================================================================
 
 
 
 
141
  export default memo(function ViewComments({
142
  comments,
143
+ onUpdate,
 
 
144
  models,
145
  }: Props) {
146
  return (
 
152
  key={`comment-${commentIdx}`}
153
  id={`comment-${commentIdx}`}
154
  comment={comment}
155
+ onEdit={(updatedComment) =>
156
+ onUpdate(comments.toSpliced(commentIdx, 1, updatedComment))
157
+ }
158
+ onDelete={() => onUpdate(comments.toSpliced(commentIdx, 1))}
159
  models={models}
160
  />
161
  );
src/components/comments/EditCommentModal.tsx ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ *
3
+ * Copyright 2023-2024 InspectorRAGet Team
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ *
17
+ **/
18
+
19
+ 'use client';
20
+
21
+ import { useState, useMemo } from 'react';
22
+ import { Modal, TextArea, TextInput, Tag } from '@carbon/react';
23
+
24
+ import { Model, TaskComment } from '@/src/types';
25
+ import classes from './AddCommentModal.module.scss';
26
+
27
+ // ===================================================================================
28
+ // TYPES
29
+ // ===================================================================================
30
+ interface Props {
31
+ comment: TaskComment;
32
+ onSubmit: Function;
33
+ onClose: Function;
34
+ open: boolean;
35
+ models: Map<string, Model> | undefined;
36
+ }
37
+
38
+ // ===================================================================================
39
+ // MAIN FUNCTION
40
+ // ===================================================================================
41
+ export default function EditCommentModal({
42
+ comment,
43
+ onSubmit,
44
+ onClose,
45
+ open = false,
46
+ models,
47
+ }: Props) {
48
+ const [commentText, setCommentText] = useState<string>(comment.comment);
49
+ const [author, setAuthor] = useState<string>('');
50
+ const [tag, tagType] = useMemo(() => {
51
+ if (comment.provenance) {
52
+ if (comment.provenance.component.includes('input')) {
53
+ return ['Input', 'purple'];
54
+ } else if (comment.provenance.component.includes('document_')) {
55
+ return ['Contexts', 'cyan'];
56
+ } else if (
57
+ comment.provenance.component.includes('::evaluation::response')
58
+ ) {
59
+ const modelId = comment.provenance.component.split('::')[0];
60
+ return [`${models?.get(modelId)?.name || modelId}`, 'green'];
61
+ } else {
62
+ return ['Generic', 'gray'];
63
+ }
64
+ } else {
65
+ return ['Generic', 'gray'];
66
+ }
67
+ }, [comment.provenance, models]);
68
+
69
+ return (
70
+ <Modal
71
+ open={open}
72
+ modalHeading="Edit comment"
73
+ modalLabel="Comments"
74
+ primaryButtonText="Save"
75
+ secondaryButtonText="Cancel"
76
+ onRequestSubmit={() => {
77
+ onSubmit({ ...comment, comment: commentText, author: author });
78
+ }}
79
+ onRequestClose={() => {
80
+ onClose();
81
+ }}
82
+ primaryButtonDisabled={commentText === comment.comment || author === ''}
83
+ >
84
+ <div className={classes.commentProvenance}>
85
+ <span className={classes.label}>Provenance</span>
86
+ <Tag className={classes.commentProvenanceTag} type={tagType}>
87
+ {tag}
88
+ </Tag>
89
+ </div>
90
+
91
+ <TextArea
92
+ className={classes.commentBox}
93
+ labelText="Comment"
94
+ rows={4}
95
+ id="comment-area"
96
+ value={commentText}
97
+ invalid={commentText === ''}
98
+ invalidText={'comment cannot be empty'}
99
+ onChange={(event) => {
100
+ setCommentText(event.target.value);
101
+ }}
102
+ />
103
+
104
+ {tag !== 'Generic' && comment.provenance?.text && (
105
+ <div className={classes.reference}>
106
+ <div>
107
+ <span className={'cds--label'}>Reference</span>
108
+ </div>
109
+ <p>{comment.provenance?.text}</p>
110
+ </div>
111
+ )}
112
+
113
+ <TextInput
114
+ id="author-input"
115
+ type="text"
116
+ labelText="Author"
117
+ defaultValue={author}
118
+ invalid={author === ''}
119
+ invalidText={'author cannot be empty'}
120
+ onChange={(event) => {
121
+ setAuthor(event.target.value);
122
+ }}
123
+ />
124
+ </Modal>
125
+ );
126
+ }
src/views/task/Task.module.scss CHANGED
@@ -164,7 +164,7 @@
164
  position: absolute;
165
  bottom: $spacing-07;
166
  right: $spacing-05;
167
- opacity: 0.5;
168
  z-index: 15;
169
  height: 3rem;
170
  width: 3rem;
@@ -173,6 +173,8 @@
173
  display: flex;
174
  align-items: center;
175
  justify-content: center;
 
 
176
  }
177
 
178
  .addCommentBtn:hover {
 
164
  position: absolute;
165
  bottom: $spacing-07;
166
  right: $spacing-05;
167
+ opacity: 0.8;
168
  z-index: 15;
169
  height: 3rem;
170
  width: 3rem;
 
173
  display: flex;
174
  align-items: center;
175
  justify-content: center;
176
+ background-color: var(--cds-background-inverse);
177
+ color: var(--cds-background);
178
  }
179
 
180
  .addCommentBtn:hover {
src/views/task/Task.tsx CHANGED
@@ -236,9 +236,11 @@ export default function Task({ taskId, onClose }: Props) {
236
  <div className={classes.commentsContainer}>
237
  <ViewComments
238
  comments={task.comments}
239
- onSelect={() => {}}
240
- onEdit={() => {}}
241
- onDelete={() => {}}
 
 
242
  models={models}
243
  ></ViewComments>
244
  </div>
 
236
  <div className={classes.commentsContainer}>
237
  <ViewComments
238
  comments={task.comments}
239
+ onUpdate={(updatedComments) => {
240
+ updateTask(taskId, {
241
+ comments: updatedComments,
242
+ });
243
+ }}
244
  models={models}
245
  ></ViewComments>
246
  </div>