tycho-components 0.40.9 → 0.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/configs/api/applyAuthErrorHandling.js +2 -3
- package/dist/configs/localization/CommentsTexts.d.ts +24 -0
- package/dist/configs/localization/CommentsTexts.js +24 -0
- package/dist/features/Comments/{CommentAdd.d.ts → CommentAdd/CommentAdd.d.ts} +6 -4
- package/dist/features/Comments/CommentAdd/CommentAdd.js +107 -0
- package/dist/features/Comments/CommentAdd/index.d.ts +2 -0
- package/dist/features/Comments/CommentAdd/index.js +2 -0
- package/dist/features/Comments/CommentAdd/style.scss +124 -0
- package/dist/features/Comments/CommentInfo.d.ts +1 -0
- package/dist/features/Comments/CommentInfo.js +8 -4
- package/dist/features/Comments/CommentService.d.ts +9 -8
- package/dist/features/Comments/CommentService.js +74 -0
- package/dist/features/Comments/CommentService.mock.d.ts +4 -0
- package/dist/features/Comments/CommentService.mock.js +67 -0
- package/dist/features/Comments/CommentThread.d.ts +11 -0
- package/dist/features/Comments/CommentThread.js +14 -0
- package/dist/features/Comments/CommentUtils.d.ts +24 -0
- package/dist/features/Comments/CommentUtils.js +175 -0
- package/dist/features/Comments/Comments.d.ts +3 -1
- package/dist/features/Comments/Comments.js +254 -49
- package/dist/features/Comments/style.scss +178 -66
- package/dist/features/Comments/types/Comment.d.ts +0 -2
- package/dist/functions/StorybookUtils.d.ts +4 -0
- package/dist/functions/StorybookUtils.js +8 -0
- package/dist/functions/index.d.ts +1 -0
- package/dist/functions/index.js +1 -0
- package/package.json +4 -3
- package/dist/features/Comments/CommentAdd.js +0 -70
|
@@ -3,9 +3,8 @@ import CookieStorage from '../CookieStorage';
|
|
|
3
3
|
import { resolveAccessDeniedMessage } from './resolveAccessDeniedMessage';
|
|
4
4
|
import { logged, message } from '../store/actions';
|
|
5
5
|
import { getCommonDispatch } from '../store/commonDispatchBridge';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
window.__STORYBOOK__);
|
|
6
|
+
import StorybookUtils from '../../functions/StorybookUtils';
|
|
7
|
+
const { isStorybook } = StorybookUtils;
|
|
9
8
|
function defaultRedirectUrls() {
|
|
10
9
|
const publicUrl = import.meta.env.VITE_APP_PUBLIC_URL;
|
|
11
10
|
return {
|
|
@@ -8,6 +8,7 @@ export declare const CommentsTexts: {
|
|
|
8
8
|
"label.button.update": string;
|
|
9
9
|
"label.requested.to": string;
|
|
10
10
|
"label.posted.on": string;
|
|
11
|
+
"label.on": string;
|
|
11
12
|
"placeholder.comments.empty": string;
|
|
12
13
|
"tooltip.edit": string;
|
|
13
14
|
"tooltip.delete": string;
|
|
@@ -29,6 +30,13 @@ export declare const CommentsTexts: {
|
|
|
29
30
|
"notification.empty": string;
|
|
30
31
|
"notification.text": string;
|
|
31
32
|
"label.edited": string;
|
|
33
|
+
"tab.write": string;
|
|
34
|
+
"tab.preview": string;
|
|
35
|
+
"placeholder.preview.empty": string;
|
|
36
|
+
"menu.undock": string;
|
|
37
|
+
"menu.dock.left": string;
|
|
38
|
+
"menu.dock.bottom": string;
|
|
39
|
+
"menu.dock.right": string;
|
|
32
40
|
};
|
|
33
41
|
"pt-BR": {
|
|
34
42
|
"label.title.comments": string;
|
|
@@ -39,6 +47,7 @@ export declare const CommentsTexts: {
|
|
|
39
47
|
"label.button.update": string;
|
|
40
48
|
"label.requested.to": string;
|
|
41
49
|
"label.posted.on": string;
|
|
50
|
+
"label.on": string;
|
|
42
51
|
"placeholder.comments.empty": string;
|
|
43
52
|
"tooltip.edit": string;
|
|
44
53
|
"tooltip.delete": string;
|
|
@@ -60,6 +69,13 @@ export declare const CommentsTexts: {
|
|
|
60
69
|
"notification.empty": string;
|
|
61
70
|
"notification.text": string;
|
|
62
71
|
"label.edited": string;
|
|
72
|
+
"tab.write": string;
|
|
73
|
+
"tab.preview": string;
|
|
74
|
+
"placeholder.preview.empty": string;
|
|
75
|
+
"menu.undock": string;
|
|
76
|
+
"menu.dock.left": string;
|
|
77
|
+
"menu.dock.bottom": string;
|
|
78
|
+
"menu.dock.right": string;
|
|
63
79
|
};
|
|
64
80
|
it: {
|
|
65
81
|
"label.title.comments": string;
|
|
@@ -70,6 +86,7 @@ export declare const CommentsTexts: {
|
|
|
70
86
|
"label.button.update": string;
|
|
71
87
|
"label.requested.to": string;
|
|
72
88
|
"label.posted.on": string;
|
|
89
|
+
"label.on": string;
|
|
73
90
|
"placeholder.comments.empty": string;
|
|
74
91
|
"tooltip.edit": string;
|
|
75
92
|
"tooltip.delete": string;
|
|
@@ -91,5 +108,12 @@ export declare const CommentsTexts: {
|
|
|
91
108
|
"notification.empty": string;
|
|
92
109
|
"notification.text": string;
|
|
93
110
|
"label.edited": string;
|
|
111
|
+
"tab.write": string;
|
|
112
|
+
"tab.preview": string;
|
|
113
|
+
"placeholder.preview.empty": string;
|
|
114
|
+
"menu.undock": string;
|
|
115
|
+
"menu.dock.left": string;
|
|
116
|
+
"menu.dock.bottom": string;
|
|
117
|
+
"menu.dock.right": string;
|
|
94
118
|
};
|
|
95
119
|
};
|
|
@@ -8,6 +8,7 @@ export const CommentsTexts = {
|
|
|
8
8
|
"label.button.update": "Update comment",
|
|
9
9
|
"label.requested.to": "Sent to:",
|
|
10
10
|
"label.posted.on": "Posted on:",
|
|
11
|
+
"label.on": "on",
|
|
11
12
|
"placeholder.comments.empty": "No comments available.",
|
|
12
13
|
"tooltip.edit": "Edit comment",
|
|
13
14
|
"tooltip.delete": "Remove comment",
|
|
@@ -29,6 +30,13 @@ export const CommentsTexts = {
|
|
|
29
30
|
"notification.empty": "No more notifications",
|
|
30
31
|
"notification.text": "{{user}} added a new comment.",
|
|
31
32
|
"label.edited": "edited",
|
|
33
|
+
"tab.write": "Write",
|
|
34
|
+
"tab.preview": "Preview",
|
|
35
|
+
"placeholder.preview.empty": "Nothing to preview",
|
|
36
|
+
"menu.undock": "Undock into separate window",
|
|
37
|
+
"menu.dock.left": "Dock to left",
|
|
38
|
+
"menu.dock.bottom": "Dock to bottom",
|
|
39
|
+
"menu.dock.right": "Dock to right",
|
|
32
40
|
},
|
|
33
41
|
"pt-BR": {
|
|
34
42
|
"label.title.comments": "Comentários",
|
|
@@ -39,6 +47,7 @@ export const CommentsTexts = {
|
|
|
39
47
|
"label.button.update": "Atualizar comentário",
|
|
40
48
|
"label.requested.to": "Enviado para:",
|
|
41
49
|
"label.posted.on": "Postado em:",
|
|
50
|
+
"label.on": "em",
|
|
42
51
|
"placeholder.comments.empty": "Nenhum comentário disponível.",
|
|
43
52
|
"tooltip.edit": "Editar comentário",
|
|
44
53
|
"tooltip.delete": "Remover comentário",
|
|
@@ -60,6 +69,13 @@ export const CommentsTexts = {
|
|
|
60
69
|
"notification.empty": "Não há notificações",
|
|
61
70
|
"notification.text": "{{user}} adicionou um novo comentário.",
|
|
62
71
|
"label.edited": "editado",
|
|
72
|
+
"tab.write": "Escrever",
|
|
73
|
+
"tab.preview": "Visualizar",
|
|
74
|
+
"placeholder.preview.empty": "Nada para visualizar",
|
|
75
|
+
"menu.undock": "Desencaixar em janela separada",
|
|
76
|
+
"menu.dock.left": "Encaixar à esquerda",
|
|
77
|
+
"menu.dock.bottom": "Encaixar embaixo",
|
|
78
|
+
"menu.dock.right": "Encaixar à direita",
|
|
63
79
|
},
|
|
64
80
|
it: {
|
|
65
81
|
"label.title.comments": "Commenti",
|
|
@@ -70,6 +86,7 @@ export const CommentsTexts = {
|
|
|
70
86
|
"label.button.update": "Aggiorna commento",
|
|
71
87
|
"label.requested.to": "Inviato a:",
|
|
72
88
|
"label.posted.on": "Pubblicato il:",
|
|
89
|
+
"label.on": "il",
|
|
73
90
|
"placeholder.comments.empty": "Nessun commento disponibile.",
|
|
74
91
|
"tooltip.edit": "Modifica commento",
|
|
75
92
|
"tooltip.delete": "Rimuovi commento",
|
|
@@ -91,5 +108,12 @@ export const CommentsTexts = {
|
|
|
91
108
|
"notification.empty": "Nessun'altra notifica",
|
|
92
109
|
"notification.text": "{{user}} ha aggiunto un commento all'inserimento {{entry}}.",
|
|
93
110
|
"label.edited": "modificato",
|
|
111
|
+
"tab.write": "Scrivi",
|
|
112
|
+
"tab.preview": "Anteprima",
|
|
113
|
+
"placeholder.preview.empty": "Niente da visualizzare",
|
|
114
|
+
"menu.undock": "Sgancia in una finestra separata",
|
|
115
|
+
"menu.dock.left": "Aggancia a sinistra",
|
|
116
|
+
"menu.dock.bottom": "Aggancia in basso",
|
|
117
|
+
"menu.dock.right": "Aggancia a destra",
|
|
94
118
|
},
|
|
95
119
|
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import '@uiw/react-markdown-preview/markdown.css';
|
|
2
|
+
import '@uiw/react-md-editor/markdown-editor.css';
|
|
3
|
+
import { User } from '../../../configs/types/User';
|
|
4
|
+
import { Comment } from '../types/Comment';
|
|
5
|
+
import './style.scss';
|
|
4
6
|
type Props = {
|
|
5
7
|
uid: string;
|
|
6
|
-
mode:
|
|
8
|
+
mode: 'lexicon' | 'corpus' | 'parser';
|
|
7
9
|
users: User[];
|
|
8
10
|
references: Record<string, string | number | boolean>;
|
|
9
11
|
keywords?: Record<string, string | number | boolean>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { yupResolver } from '@hookform/resolvers/yup';
|
|
3
|
+
import Tab from '@mui/material/Tab';
|
|
4
|
+
import Tabs from '@mui/material/Tabs';
|
|
5
|
+
import '@uiw/react-markdown-preview/markdown.css';
|
|
6
|
+
import MDEditor from '@uiw/react-md-editor';
|
|
7
|
+
import '@uiw/react-md-editor/markdown-editor.css';
|
|
8
|
+
import { useState } from 'react';
|
|
9
|
+
import { useForm } from 'react-hook-form';
|
|
10
|
+
import { useTranslation } from 'react-i18next';
|
|
11
|
+
import { Button, IconButton, SelectField } from 'tycho-storybook';
|
|
12
|
+
import * as yup from 'yup';
|
|
13
|
+
import { useMessageUtils } from '../../../configs/useMessageUtils';
|
|
14
|
+
import FormUtils from '../../../functions/FormUtils';
|
|
15
|
+
import CommentService from '../CommentService';
|
|
16
|
+
import { EMPTY_COMMENT_REQUEST, } from '../types/Comment';
|
|
17
|
+
import './style.scss';
|
|
18
|
+
const EMPTY_COMMENT_ADD_FORM = {
|
|
19
|
+
...EMPTY_COMMENT_REQUEST,
|
|
20
|
+
requested: [],
|
|
21
|
+
userPicker: '',
|
|
22
|
+
};
|
|
23
|
+
export default function CommentAdd({ uid, mode, users, references, keywords, comment, reply, onClose, onChange, }) {
|
|
24
|
+
const { t } = useTranslation('comments');
|
|
25
|
+
const { isLoading, dispatchLoading } = useMessageUtils();
|
|
26
|
+
const [activeTab, setActiveTab] = useState('write');
|
|
27
|
+
const getFormDefaultValues = () => {
|
|
28
|
+
if (comment)
|
|
29
|
+
return {
|
|
30
|
+
...EMPTY_COMMENT_ADD_FORM,
|
|
31
|
+
value: comment.value,
|
|
32
|
+
requested: comment.requested?.map((r) => r.uid) ?? [],
|
|
33
|
+
};
|
|
34
|
+
if (reply) {
|
|
35
|
+
return {
|
|
36
|
+
...EMPTY_COMMENT_ADD_FORM,
|
|
37
|
+
reply: reply.id,
|
|
38
|
+
requested: [reply.user],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return EMPTY_COMMENT_ADD_FORM;
|
|
42
|
+
};
|
|
43
|
+
const createdForm = useForm({
|
|
44
|
+
resolver: yupResolver(getFormSchema(t)),
|
|
45
|
+
mode: 'onChange',
|
|
46
|
+
defaultValues: getFormDefaultValues(),
|
|
47
|
+
});
|
|
48
|
+
const value = createdForm.watch('value') ?? '';
|
|
49
|
+
const requested = createdForm.watch('requested') ?? [];
|
|
50
|
+
const toCommentRequest = (form) => {
|
|
51
|
+
const { userPicker: _userPicker, ...request } = form;
|
|
52
|
+
return request;
|
|
53
|
+
};
|
|
54
|
+
const handleAdd = () => {
|
|
55
|
+
if (isLoading())
|
|
56
|
+
return;
|
|
57
|
+
dispatchLoading(true);
|
|
58
|
+
CommentService.add(uid, mode, toCommentRequest(createdForm.getValues()), references, keywords).then((r) => {
|
|
59
|
+
dispatchLoading(false);
|
|
60
|
+
onClose();
|
|
61
|
+
onChange(r.data);
|
|
62
|
+
createdForm.reset(EMPTY_COMMENT_ADD_FORM);
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const handleUpdate = () => {
|
|
66
|
+
if (!comment)
|
|
67
|
+
return;
|
|
68
|
+
CommentService.update(comment.id, toCommentRequest(createdForm.getValues())).then((r) => {
|
|
69
|
+
onClose();
|
|
70
|
+
onChange(r.data);
|
|
71
|
+
createdForm.reset(EMPTY_COMMENT_ADD_FORM);
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const handleUserPick = (_attr, nextValue) => {
|
|
75
|
+
if (!nextValue)
|
|
76
|
+
return;
|
|
77
|
+
const current = createdForm.getValues('requested') ?? [];
|
|
78
|
+
if (!current.includes(nextValue)) {
|
|
79
|
+
createdForm.setValue('requested', [...current, nextValue], {
|
|
80
|
+
shouldDirty: true,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
createdForm.setValue('userPicker', '');
|
|
84
|
+
};
|
|
85
|
+
const handleRemoveUser = (uidToRemove) => {
|
|
86
|
+
const current = createdForm.getValues('requested') ?? [];
|
|
87
|
+
createdForm.setValue('requested', current.filter((id) => id !== uidToRemove), { shouldDirty: true });
|
|
88
|
+
};
|
|
89
|
+
const availableUsers = users.filter((user) => !requested.includes(user.uid));
|
|
90
|
+
return (_jsxs("div", { className: "comment-add-container", children: [_jsx("div", { className: "form-tabs", children: _jsxs(Tabs, { value: activeTab, onChange: (_, next) => setActiveTab(next), children: [_jsx(Tab, { label: t('tab.write'), value: "write" }), _jsx(Tab, { label: t('tab.preview'), value: "preview" })] }) }), activeTab === 'write' ? (_jsx("div", { className: "form-editor", "data-color-mode": "light", children: _jsx(MDEditor, { height: 220, value: value, preview: "edit", onChange: (next) => createdForm.setValue('value', next || '', {
|
|
91
|
+
shouldValidate: true,
|
|
92
|
+
shouldDirty: true,
|
|
93
|
+
}), textareaProps: {
|
|
94
|
+
placeholder: t('placeholder.comment.text'),
|
|
95
|
+
} }) })) : (_jsx("div", { className: "form-preview", "data-color-mode": "light", children: value.trim() ? (_jsx(MDEditor.Markdown, { source: value })) : (_jsx("span", { className: "preview-empty", children: t('placeholder.preview.empty') })) })), _jsxs("div", { className: "form-footer", children: [_jsx(SelectField, { label: reply ? t('input.user.reply') : t('input.user'), attr: "userPicker", createdForm: createdForm, placeholder: t('common:generic.placeholder.select'), options: FormUtils.convertList(availableUsers, 'name', 'uid'), showEndAdornment: false, onChange: handleUserPick }), requested.length > 0 && (_jsx("div", { className: "selected-users", children: requested.map((userUid) => {
|
|
96
|
+
const user = users.find((u) => u.uid === userUid);
|
|
97
|
+
return (_jsxs("div", { className: "selected-user", children: [_jsx("span", { className: "name", children: user?.name || userUid }), _jsx(IconButton, { name: "close", size: "x-small", mode: "ghost", onClick: () => handleRemoveUser(userUid) })] }, userUid));
|
|
98
|
+
}) })), _jsxs("div", { className: "buttons", children: [_jsx(Button, { onClick: onClose, text: t('button.discard'), color: "danger", size: "small" }), _jsx(Button, { onClick: comment ? handleUpdate : handleAdd, text: comment ? t('label.button.update') : t('label.button.add'), disabled: !createdForm.formState.isValid, size: "small" })] })] })] }));
|
|
99
|
+
}
|
|
100
|
+
const getFormSchema = (t) => yup.object().shape({
|
|
101
|
+
id: yup.string().optional(),
|
|
102
|
+
value: yup.string().required(t('common:validation.required')),
|
|
103
|
+
requested: yup.array().of(yup.string().defined()).optional(),
|
|
104
|
+
coordinates: yup.array().of(yup.string().defined()).optional(),
|
|
105
|
+
userPicker: yup.string().optional(),
|
|
106
|
+
reply: yup.string().optional(),
|
|
107
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
.comment-add-container {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
margin-bottom: var(--spacing-200);
|
|
5
|
+
border: 1px solid var(--border-subtle-1);
|
|
6
|
+
border-radius: var(--radius-200);
|
|
7
|
+
overflow: hidden;
|
|
8
|
+
background-color: var(--background-default);
|
|
9
|
+
margin-top: 16px;
|
|
10
|
+
|
|
11
|
+
.form-tabs {
|
|
12
|
+
padding: 0 var(--spacing-150);
|
|
13
|
+
background-color: var(--layer-hover-2);
|
|
14
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
15
|
+
|
|
16
|
+
.MuiTabs-root {
|
|
17
|
+
min-height: unset;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.MuiTabs-indicator {
|
|
21
|
+
background-color: var(--border-hover);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.MuiTab-root {
|
|
25
|
+
@include label-medium-1;
|
|
26
|
+
min-height: unset;
|
|
27
|
+
min-width: unset;
|
|
28
|
+
padding: var(--spacing-100) var(--spacing-150);
|
|
29
|
+
text-transform: none;
|
|
30
|
+
color: var(--text-secondary);
|
|
31
|
+
|
|
32
|
+
&.Mui-selected {
|
|
33
|
+
color: var(--text-primary);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.form-editor {
|
|
39
|
+
.w-md-editor {
|
|
40
|
+
border: none;
|
|
41
|
+
border-radius: 0;
|
|
42
|
+
box-shadow: none;
|
|
43
|
+
|
|
44
|
+
.w-md-editor-toolbar {
|
|
45
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
46
|
+
background-color: var(--background-default);
|
|
47
|
+
padding: var(--spacing-050) var(--spacing-100);
|
|
48
|
+
|
|
49
|
+
li button {
|
|
50
|
+
padding: 0 var(--spacing-050);
|
|
51
|
+
|
|
52
|
+
> svg {
|
|
53
|
+
width: 16px;
|
|
54
|
+
height: 16px;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.w-md-editor-content {
|
|
60
|
+
background-color: var(--background-default);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.form-preview {
|
|
66
|
+
min-height: 220px;
|
|
67
|
+
padding: var(--spacing-150);
|
|
68
|
+
background-color: var(--background-default);
|
|
69
|
+
|
|
70
|
+
.preview-empty {
|
|
71
|
+
@include helper-small-1;
|
|
72
|
+
color: var(--text-tertiary);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.wmde-markdown {
|
|
76
|
+
background: transparent;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.form-footer {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
gap: var(--spacing-100);
|
|
84
|
+
padding: var(--spacing-150);
|
|
85
|
+
border-top: 1px solid var(--border-subtle-1);
|
|
86
|
+
background-color: var(--background-default);
|
|
87
|
+
|
|
88
|
+
> .ds-select-text {
|
|
89
|
+
margin-bottom: 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.selected-users {
|
|
93
|
+
display: flex;
|
|
94
|
+
flex-wrap: wrap;
|
|
95
|
+
gap: var(--spacing-100);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.selected-user {
|
|
99
|
+
display: inline-flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
gap: var(--spacing-050);
|
|
102
|
+
padding: 4px 8px;
|
|
103
|
+
border: 1px solid var(--border-subtle-1);
|
|
104
|
+
border-radius: var(--radius-100);
|
|
105
|
+
background-color: var(--layer-hover-2);
|
|
106
|
+
|
|
107
|
+
.name {
|
|
108
|
+
@include label-small-1;
|
|
109
|
+
color: var(--text-primary);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.ds-icon-button.x-small {
|
|
113
|
+
width: 16px;
|
|
114
|
+
height: 16px;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
> .buttons {
|
|
119
|
+
display: flex;
|
|
120
|
+
justify-content: flex-end;
|
|
121
|
+
gap: var(--spacing-100);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import MDEditor from '@uiw/react-md-editor';
|
|
3
|
+
import '@uiw/react-markdown-preview/markdown.css';
|
|
2
4
|
import { useContext } from 'react';
|
|
3
5
|
import { useTranslation } from 'react-i18next';
|
|
4
|
-
import { Avatar, IconButton } from 'tycho-storybook';
|
|
6
|
+
import { Avatar, IconButton, Tag } from 'tycho-storybook';
|
|
5
7
|
import CommonContext from '../../configs/CommonContext';
|
|
6
8
|
import DateUtils from '../../functions/DateUtils';
|
|
7
|
-
import './style.scss';
|
|
8
9
|
import CommentService from './CommentService';
|
|
10
|
+
import './style.scss';
|
|
9
11
|
export default function CommentInfo({ comment, hasEditAccess = true, onRemove, onUpdate, onEdit, onReply, }) {
|
|
10
12
|
const { t } = useTranslation('comments');
|
|
11
13
|
const { state } = useContext(CommonContext);
|
|
@@ -27,5 +29,7 @@ export default function CommentInfo({ comment, hasEditAccess = true, onRemove, o
|
|
|
27
29
|
return false;
|
|
28
30
|
return !requested.read;
|
|
29
31
|
};
|
|
30
|
-
|
|
32
|
+
const displayDate = comment.edited || comment.date;
|
|
33
|
+
const fullDateTitle = DateUtils.formatDateTime(displayDate, 'MM/dd/yyyy - HH:mm');
|
|
34
|
+
return (_jsxs("div", { className: "comment", children: [_jsxs("div", { className: "comment-header", children: [_jsx(Avatar, { title: comment.name, src: comment.picture, size: "x-small" }), _jsxs("div", { className: "meta", children: [_jsx("span", { className: "name", children: comment.name }), _jsxs("span", { className: "timestamp", title: fullDateTitle, children: [t('label.on'), ' ', DateUtils.formatDateTime(displayDate, 'MMM d')] })] }), _jsxs("div", { className: "buttons", children: [comment.edited && (_jsx(Tag, { text: t('label.edited'), color: "blue", size: "small" })), hasEditAccess && !comment.reply && (_jsx(IconButton, { size: "x-small", onClick: onReply, title: t('tooltip.reply'), name: "reply" })), hasEditAccess && state.logged?.uid === comment.user && (_jsxs(_Fragment, { children: [_jsx(IconButton, { size: "x-small", onClick: onEdit, title: t('tooltip.edit'), name: "edit" }), _jsx(IconButton, { size: "x-small", onClick: handleRemove, title: t('tooltip.delete'), name: "delete" })] })), isNotRead(comment) && (_jsx(IconButton, { size: "x-small", onClick: handleMarkRead, title: t('tooltip.read'), name: "check_circle" }))] })] }), _jsxs("div", { className: "comment-body", children: [_jsx("div", { className: "text", "data-color-mode": "light", children: _jsx(MDEditor.Markdown, { source: comment.value }) }), comment.requested && (_jsxs("div", { className: "meta-line", children: [_jsx("span", { className: "me-1", children: t('label.requested.to') }), _jsx("span", { children: comment.requested.map((e) => e.name).join(', ') })] }))] })] }));
|
|
31
35
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { AxiosResponse } from 'axios';
|
|
1
2
|
import { User } from '../../configs/types/User';
|
|
2
3
|
import { Comment, CommentRequest } from './types/Comment';
|
|
3
|
-
declare function findNotifications(uid: string, mode: string): Promise<
|
|
4
|
-
declare function findReadNotifications(uid: string, mode: string): Promise<
|
|
5
|
-
declare function markRead(id: string): Promise<
|
|
6
|
-
declare function update(id: string, comment: CommentRequest): Promise<
|
|
7
|
-
declare function remove(id: string): Promise<
|
|
8
|
-
declare function add(uid: string, mode: string, comment: CommentRequest, references: Record<string, string | number | boolean>, keywords?: Record<string, string | number | boolean>): Promise<
|
|
9
|
-
declare function find(uid: string, mode: string, request: Record<string, string | number | boolean>): Promise<
|
|
10
|
-
declare function findAvailableUsers(uid: string, mode: string): Promise<
|
|
4
|
+
declare function findNotifications(uid: string, mode: string): Promise<AxiosResponse<Comment[], any, {}, any>>;
|
|
5
|
+
declare function findReadNotifications(uid: string, mode: string): Promise<AxiosResponse<Comment[], any, {}, any>>;
|
|
6
|
+
declare function markRead(id: string): Promise<AxiosResponse<Comment, any, {}, any>>;
|
|
7
|
+
declare function update(id: string, comment: CommentRequest): Promise<AxiosResponse<Comment, any, {}, any>>;
|
|
8
|
+
declare function remove(id: string): Promise<AxiosResponse<Comment, any, {}, any>>;
|
|
9
|
+
declare function add(uid: string, mode: string, comment: CommentRequest, references: Record<string, string | number | boolean>, keywords?: Record<string, string | number | boolean>): Promise<AxiosResponse<Comment, any, {}, any>>;
|
|
10
|
+
declare function find(uid: string, mode: string, request: Record<string, string | number | boolean>): Promise<AxiosResponse<Comment[], any, {}, any>>;
|
|
11
|
+
declare function findAvailableUsers(uid: string, mode: string): Promise<AxiosResponse<User[], any, {}, any>>;
|
|
11
12
|
declare const CommentService: {
|
|
12
13
|
add: typeof add;
|
|
13
14
|
remove: typeof remove;
|
|
@@ -1,4 +1,30 @@
|
|
|
1
1
|
import api from '../../configs/api';
|
|
2
|
+
import StorybookUtils from '../../functions/StorybookUtils';
|
|
3
|
+
import { mockCommentUsers, mockComments } from './CommentService.mock';
|
|
4
|
+
let storybookComments = mockComments.map((c) => ({ ...c }));
|
|
5
|
+
function mockResponse(data) {
|
|
6
|
+
return Promise.resolve({
|
|
7
|
+
data,
|
|
8
|
+
status: 200,
|
|
9
|
+
statusText: 'OK',
|
|
10
|
+
headers: {},
|
|
11
|
+
config: {},
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function resolveRequestedUsers(uids) {
|
|
15
|
+
if (!uids)
|
|
16
|
+
return undefined;
|
|
17
|
+
return uids.map((uid) => {
|
|
18
|
+
const user = mockCommentUsers.find((u) => u.uid === uid);
|
|
19
|
+
return {
|
|
20
|
+
uid,
|
|
21
|
+
name: user?.name || uid,
|
|
22
|
+
picture: user?.picture || '',
|
|
23
|
+
email: user?.sub || '',
|
|
24
|
+
read: false,
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
}
|
|
2
28
|
function findNotifications(uid, mode) {
|
|
3
29
|
return api.get(`${import.meta.env.VITE_APP_COMMENT_API_URL}/${mode}/notifications/${uid}`);
|
|
4
30
|
}
|
|
@@ -6,15 +32,57 @@ function findReadNotifications(uid, mode) {
|
|
|
6
32
|
return api.get(`${import.meta.env.VITE_APP_COMMENT_API_URL}/${mode}/notifications/read/${uid}`);
|
|
7
33
|
}
|
|
8
34
|
function markRead(id) {
|
|
35
|
+
if (StorybookUtils.isStorybook()) {
|
|
36
|
+
storybookComments = storybookComments.map((c) => c.id === id
|
|
37
|
+
? { ...c, requested: c.requested?.map((r) => ({ ...r, read: true })) }
|
|
38
|
+
: c);
|
|
39
|
+
return mockResponse(storybookComments.find((c) => c.id === id));
|
|
40
|
+
}
|
|
9
41
|
return api.put(`${import.meta.env.VITE_APP_COMMENT_API_URL}/read/${id}`);
|
|
10
42
|
}
|
|
11
43
|
function update(id, comment) {
|
|
44
|
+
if (StorybookUtils.isStorybook()) {
|
|
45
|
+
storybookComments = storybookComments.map((c) => {
|
|
46
|
+
if (c.id !== id)
|
|
47
|
+
return c;
|
|
48
|
+
return {
|
|
49
|
+
...c,
|
|
50
|
+
value: comment.value ?? c.value,
|
|
51
|
+
requested: comment.requested
|
|
52
|
+
? resolveRequestedUsers(comment.requested)
|
|
53
|
+
: c.requested,
|
|
54
|
+
coordinates: comment.coordinates ?? c.coordinates,
|
|
55
|
+
edited: new Date().toISOString(),
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
return mockResponse(storybookComments.find((c) => c.id === id));
|
|
59
|
+
}
|
|
12
60
|
return api.patch(`${import.meta.env.VITE_APP_COMMENT_API_URL}/${id}`, { ...comment });
|
|
13
61
|
}
|
|
14
62
|
function remove(id) {
|
|
63
|
+
if (StorybookUtils.isStorybook()) {
|
|
64
|
+
storybookComments = storybookComments.filter((c) => c.id !== id);
|
|
65
|
+
return mockResponse(undefined);
|
|
66
|
+
}
|
|
15
67
|
return api.delete(`${import.meta.env.VITE_APP_COMMENT_API_URL}/${id}`);
|
|
16
68
|
}
|
|
17
69
|
function add(uid, mode, comment, references, keywords) {
|
|
70
|
+
if (StorybookUtils.isStorybook()) {
|
|
71
|
+
const replyId = comment.reply;
|
|
72
|
+
const newComment = {
|
|
73
|
+
id: `mock-comment-${Date.now()}`,
|
|
74
|
+
value: comment.value || '',
|
|
75
|
+
date: new Date().toISOString(),
|
|
76
|
+
name: 'Storybook User',
|
|
77
|
+
user: 'storybook-current-user',
|
|
78
|
+
picture: '',
|
|
79
|
+
reply: replyId,
|
|
80
|
+
requested: resolveRequestedUsers(comment.requested),
|
|
81
|
+
coordinates: comment.coordinates,
|
|
82
|
+
};
|
|
83
|
+
storybookComments = [...storybookComments, newComment];
|
|
84
|
+
return mockResponse(newComment);
|
|
85
|
+
}
|
|
18
86
|
return api.post(`${import.meta.env.VITE_APP_COMMENT_API_URL}/${mode}/${uid}`, {
|
|
19
87
|
...comment,
|
|
20
88
|
...references,
|
|
@@ -22,9 +90,15 @@ function add(uid, mode, comment, references, keywords) {
|
|
|
22
90
|
});
|
|
23
91
|
}
|
|
24
92
|
function find(uid, mode, request) {
|
|
93
|
+
if (StorybookUtils.isStorybook()) {
|
|
94
|
+
return mockResponse(storybookComments);
|
|
95
|
+
}
|
|
25
96
|
return api.post(`${import.meta.env.VITE_APP_COMMENT_API_URL}/${mode}/find/${uid}`, request);
|
|
26
97
|
}
|
|
27
98
|
function findAvailableUsers(uid, mode) {
|
|
99
|
+
if (StorybookUtils.isStorybook()) {
|
|
100
|
+
return mockResponse(mockCommentUsers);
|
|
101
|
+
}
|
|
28
102
|
return api.get(`${import.meta.env.VITE_APP_AUTH_API}/${mode}/${uid}`);
|
|
29
103
|
}
|
|
30
104
|
const CommentService = {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { UserStatus } from '../../configs/types/User';
|
|
2
|
+
export const mockCommentUsers = [
|
|
3
|
+
{
|
|
4
|
+
uid: 'storybook-user-ana',
|
|
5
|
+
name: 'Ana Silva',
|
|
6
|
+
sub: 'ana.silva@example.com',
|
|
7
|
+
picture: '',
|
|
8
|
+
lang: 'en',
|
|
9
|
+
status: UserStatus.ACTIVE,
|
|
10
|
+
permissions: [],
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
uid: 'storybook-user-john',
|
|
14
|
+
name: 'John Doe',
|
|
15
|
+
sub: 'john.doe@example.com',
|
|
16
|
+
picture: '',
|
|
17
|
+
lang: 'en',
|
|
18
|
+
status: UserStatus.ACTIVE,
|
|
19
|
+
permissions: [],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
uid: 'storybook-user-maria',
|
|
23
|
+
name: 'Maria Souza',
|
|
24
|
+
sub: 'maria.souza@example.com',
|
|
25
|
+
picture: '',
|
|
26
|
+
lang: 'en',
|
|
27
|
+
status: UserStatus.ACTIVE,
|
|
28
|
+
permissions: [],
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
export const mockComments = [
|
|
32
|
+
{
|
|
33
|
+
id: 'mock-comment-1',
|
|
34
|
+
value: 'This passage needs a **second review** before publishing.\n\n```\ncheck coordinates\n```',
|
|
35
|
+
date: new Date(Date.now() - 2 * 86400000).toISOString(),
|
|
36
|
+
name: 'Ana Silva',
|
|
37
|
+
user: 'storybook-user-ana',
|
|
38
|
+
picture: '',
|
|
39
|
+
edited: new Date(Date.now() - 86400000).toISOString(),
|
|
40
|
+
requested: [
|
|
41
|
+
{
|
|
42
|
+
uid: 'storybook-user-john',
|
|
43
|
+
name: 'John Doe',
|
|
44
|
+
picture: '',
|
|
45
|
+
email: 'john.doe@example.com',
|
|
46
|
+
read: false,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: 'mock-comment-1-reply',
|
|
52
|
+
value: 'Agreed, I will take a look today.',
|
|
53
|
+
date: new Date(Date.now() - 86400000).toISOString(),
|
|
54
|
+
name: 'John Doe',
|
|
55
|
+
user: 'storybook-user-john',
|
|
56
|
+
picture: '',
|
|
57
|
+
reply: 'mock-comment-1',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'mock-comment-2',
|
|
61
|
+
value: 'Great work on this entry!',
|
|
62
|
+
date: new Date(Date.now() - 3 * 3600000).toISOString(),
|
|
63
|
+
name: 'Maria Souza',
|
|
64
|
+
user: 'storybook-user-maria',
|
|
65
|
+
picture: '',
|
|
66
|
+
},
|
|
67
|
+
];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Comment } from './types/Comment';
|
|
2
|
+
type Props = {
|
|
3
|
+
comments: Comment[];
|
|
4
|
+
hasEditAccess: boolean;
|
|
5
|
+
onRemove: (id: Comment['id']) => void;
|
|
6
|
+
onUpdate: (comment: Comment) => void;
|
|
7
|
+
onEdit: (comment: Comment) => void;
|
|
8
|
+
onReply: (comment: Comment) => void;
|
|
9
|
+
};
|
|
10
|
+
export default function CommentThread({ comments, hasEditAccess, onRemove, onUpdate, onEdit, onReply, }: Props): import("react").JSX.Element;
|
|
11
|
+
export {};
|