tycho-components 0.0.10-SNAPSHOT-7 → 0.0.11-SNAPSHOT
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/AppLoading/AppLoading.d.ts +3 -0
- package/dist/AppLoading/AppLoading.js +7 -0
- package/dist/AppLoading/index.d.ts +2 -0
- package/dist/AppLoading/index.js +2 -0
- package/dist/AppLoading/style.scss +8 -0
- package/dist/AppPlaceholder/AppPlaceholder.d.ts +8 -0
- package/dist/AppPlaceholder/AppPlaceholder.js +5 -0
- package/dist/AppPlaceholder/index.d.ts +2 -0
- package/dist/AppPlaceholder/index.js +2 -0
- package/dist/AppPlaceholder/style.scss +18 -0
- package/dist/Comments/Comments.d.ts +9 -0
- package/dist/Comments/Comments.js +110 -0
- package/dist/Comments/HeaderNotifications/HeaderNotifications.d.ts +6 -0
- package/dist/Comments/HeaderNotifications/HeaderNotifications.js +39 -0
- package/dist/Comments/HeaderNotifications/index.d.ts +2 -0
- package/dist/Comments/HeaderNotifications/index.js +2 -0
- package/dist/Comments/HeaderNotifications/style.scss +104 -0
- package/dist/Comments/index.d.ts +2 -0
- package/dist/Comments/index.js +2 -0
- package/dist/Comments/style.scss +136 -0
- package/dist/Comments/types/Comment.d.ts +28 -0
- package/dist/Comments/types/Comment.js +13 -0
- package/dist/Comments/types/CommentService.d.ts +21 -0
- package/dist/Comments/types/CommentService.js +44 -0
- package/dist/VirtualKeyboard/KeyboardCustomLayout.d.ts +8 -0
- package/dist/VirtualKeyboard/KeyboardCustomLayout.js +25 -0
- package/dist/VirtualKeyboard/VirtualKeyboard.d.ts +9 -0
- package/dist/VirtualKeyboard/VirtualKeyboard.js +68 -0
- package/dist/VirtualKeyboard/index.d.ts +2 -0
- package/dist/VirtualKeyboard/index.js +2 -0
- package/dist/VirtualKeyboard/style.scss +33 -0
- package/dist/configs/Localization.d.ts +56 -0
- package/dist/configs/Localization.js +4 -1
- package/dist/configs/User.d.ts +16 -0
- package/dist/configs/User.js +7 -0
- package/dist/configs/localization/CommentsTexts.d.ts +58 -0
- package/dist/configs/localization/CommentsTexts.js +58 -0
- package/dist/configs/store/actions.d.ts +2 -0
- package/dist/configs/store/actions.js +4 -0
- package/dist/configs/store/reducer.js +5 -0
- package/dist/configs/store/store.js +1 -0
- package/dist/configs/store/types.d.ts +4 -1
- package/dist/configs/store/types.js +1 -0
- package/dist/functions/DateUtils.d.ts +9 -0
- package/dist/functions/DateUtils.js +42 -0
- package/dist/functions/FormUtils.d.ts +11 -0
- package/dist/functions/FormUtils.js +56 -0
- package/dist/functions/SecurityUtils.d.ts +6 -0
- package/dist/functions/SecurityUtils.js +28 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +8 -1
- package/package.json +5 -2
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import ReactLoading from 'react-loading';
|
|
3
|
+
import './style.scss';
|
|
4
|
+
function AppLoading() {
|
|
5
|
+
return (_jsx("div", { className: "loading-container", children: _jsx(ReactLoading, { type: "spinningBubbles", color: "blue", height: 50, width: 50 }) }));
|
|
6
|
+
}
|
|
7
|
+
export default AppLoading;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import './style.scss';
|
|
3
|
+
export default function AppPlaceholder({ children, onClick }) {
|
|
4
|
+
return (_jsx("div", { role: "presentation", className: "placeholder-container", onClick: () => onClick && onClick(), onKeyDown: () => onClick && onClick(), children: children }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.placeholder-container {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
justify-content: center;
|
|
5
|
+
align-items: center;
|
|
6
|
+
cursor: pointer;
|
|
7
|
+
|
|
8
|
+
margin: 32px 16px;
|
|
9
|
+
font-size: var(--font-size-xlarge);
|
|
10
|
+
|
|
11
|
+
height: 75%;
|
|
12
|
+
padding: var(--spacing-small);
|
|
13
|
+
background-color: var(--opacity-black-08);
|
|
14
|
+
justify-content: center;
|
|
15
|
+
align-items: center;
|
|
16
|
+
font-weight: var(--font-weight-large);
|
|
17
|
+
color: var(--color-primary-alternative);
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import './style.scss';
|
|
2
|
+
type Props = {
|
|
3
|
+
lexicon: string;
|
|
4
|
+
entry: string;
|
|
5
|
+
keywords: Record<string, string | number | boolean>;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
};
|
|
8
|
+
export default function Comments({ lexicon, entry, keywords, onClose }: Props): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { yupResolver } from '@hookform/resolvers/yup';
|
|
3
|
+
import { Drawer } from '@mui/material';
|
|
4
|
+
import { useContext, useEffect, useMemo, useState } from 'react';
|
|
5
|
+
import { useForm } from 'react-hook-form';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { Avatar, Button, IconButton, SelectField, TextField, } from 'tycho-storybook';
|
|
8
|
+
import * as yup from 'yup';
|
|
9
|
+
import AppLoading from '../AppLoading';
|
|
10
|
+
import AppPlaceholder from '../AppPlaceholder';
|
|
11
|
+
import CommonContext from '../configs/CommonContext';
|
|
12
|
+
import { useMessageUtils } from '../configs/useMessageUtils';
|
|
13
|
+
import DateUtils from '../functions/DateUtils';
|
|
14
|
+
import FormUtils from '../functions/FormUtils';
|
|
15
|
+
import SecurityUtils from '../functions/SecurityUtils';
|
|
16
|
+
import './style.scss';
|
|
17
|
+
import { EMPTY_COMMENT_REQUEST, } from './types/Comment';
|
|
18
|
+
import CommentService from './types/CommentService';
|
|
19
|
+
export default function Comments({ lexicon, entry, keywords, onClose }) {
|
|
20
|
+
const { t } = useTranslation('comments');
|
|
21
|
+
const { state } = useContext(CommonContext);
|
|
22
|
+
const { isLoading, dispatchLoading } = useMessageUtils();
|
|
23
|
+
const [openAddComment, setOpenAddComment] = useState(false);
|
|
24
|
+
const [comment, setComment] = useState();
|
|
25
|
+
const [comments, setComments] = useState();
|
|
26
|
+
const [users, setUsers] = useState([]);
|
|
27
|
+
const createdForm = useForm({
|
|
28
|
+
resolver: yupResolver(getFormSchema(t)),
|
|
29
|
+
mode: 'onChange',
|
|
30
|
+
});
|
|
31
|
+
const load = async () => {
|
|
32
|
+
try {
|
|
33
|
+
const [commentsResponse, usersResponse] = await Promise.all([
|
|
34
|
+
CommentService.find(lexicon, entry),
|
|
35
|
+
CommentService.findAvailableUsers(lexicon),
|
|
36
|
+
]);
|
|
37
|
+
setComments(commentsResponse.data);
|
|
38
|
+
setUsers(usersResponse.data);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error('Error loading comments or users', error);
|
|
42
|
+
// optionally handle specific error state here
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const handleAdd = () => {
|
|
46
|
+
if (isLoading())
|
|
47
|
+
return;
|
|
48
|
+
dispatchLoading(true);
|
|
49
|
+
CommentService.add(lexicon, entry, createdForm.getValues(), keywords).then((r) => {
|
|
50
|
+
dispatchLoading(false);
|
|
51
|
+
setOpenAddComment(false);
|
|
52
|
+
setComment(undefined);
|
|
53
|
+
setComments((prev) => [...(prev || []), r.data]);
|
|
54
|
+
createdForm.reset(EMPTY_COMMENT_REQUEST);
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
const handleMarkRead = (thisComment) => {
|
|
58
|
+
CommentService.markRead(thisComment.id).then(() => {
|
|
59
|
+
const updated = { ...thisComment, read: true };
|
|
60
|
+
setComments(comments?.map((c) => c.id === thisComment.id ? { ...c, ...updated } : c));
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
const handleRemove = (thisComment) => {
|
|
64
|
+
CommentService.remove(thisComment.id).then(() => {
|
|
65
|
+
setComments(comments?.filter((c) => c.id !== thisComment.id));
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const handleEdit = (thisComment) => {
|
|
69
|
+
setComment(thisComment);
|
|
70
|
+
setOpenAddComment(true);
|
|
71
|
+
createdForm.reset({
|
|
72
|
+
value: thisComment.value,
|
|
73
|
+
title: thisComment.title,
|
|
74
|
+
requestedUser: thisComment.requested?.uid,
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
const handleUpdate = () => {
|
|
78
|
+
if (!comment)
|
|
79
|
+
return;
|
|
80
|
+
CommentService.update(comment.id, createdForm.getValues()).then((r) => {
|
|
81
|
+
setComments((prev) => prev?.map((c) => (c.id === comment.id ? r.data : c)));
|
|
82
|
+
setComment(undefined);
|
|
83
|
+
setOpenAddComment(false);
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
const hasEditAccess = useMemo(() => {
|
|
87
|
+
return SecurityUtils.hasAccess(lexicon, ['ADMIN', 'EDITOR']);
|
|
88
|
+
}, [lexicon]);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
load();
|
|
91
|
+
}, []);
|
|
92
|
+
if (!comments)
|
|
93
|
+
return _jsx(AppLoading, {});
|
|
94
|
+
return (_jsx(Drawer, { anchor: "right", open: true, onClose: onClose, children: _jsxs("div", { className: "comments-container", children: [_jsxs("div", { className: "header", children: [_jsx(IconButton, { name: "close", size: "small", mode: "ghost", onClick: onClose }), _jsx("span", { className: "title", children: t('label.title.comments') }), _jsx("div", { className: "actions", children: hasEditAccess && (_jsx(Button, { text: t('button.add'), icon: "add", mode: "outlined", size: "small", className: "edit-button", onClick: () => {
|
|
95
|
+
setComment(undefined);
|
|
96
|
+
setOpenAddComment(!openAddComment);
|
|
97
|
+
} })) })] }), _jsxs("div", { className: "body", children: [openAddComment && (_jsxs("div", { className: "form", children: [_jsx(TextField, { label: t('input.value'), attr: "value", createdForm: createdForm, showEndAdornment: false, placeholder: t('common:generic.placeholder'), required: true, multiline: true }), _jsx(TextField, { label: t('input.title'), attr: "title", createdForm: createdForm, showEndAdornment: false, placeholder: t('common:generic.placeholder') }), _jsx(SelectField, { label: t('input.user'), attr: "requestedUser", createdForm: createdForm, options: [
|
|
98
|
+
{ label: t('common:generic.placeholder.select'), value: '' },
|
|
99
|
+
...FormUtils.convertList(users, 'name', 'uid'),
|
|
100
|
+
], showEndAdornment: false }), _jsxs("div", { className: "buttons", children: [comment && (_jsx(Button, { onClick: comment?.id ? handleUpdate : handleAdd, text: t('button.discard'), color: "danger" })), _jsx(Button, { onClick: comment ? handleUpdate : handleAdd, text: comment ? t('label.button.update') : t('label.button.add'), disabled: !createdForm.formState.isValid })] })] })), comments?.length === 0 && (_jsx(AppPlaceholder, { children: _jsx("span", { children: t('placeholder.comments.empty') }) })), comments?.map((el, idx) => (_jsxs("div", { className: "comment", children: [_jsxs("div", { className: "top", children: [_jsx(Avatar, { title: el.name, src: el.picture, size: "medium" }), _jsx("div", { className: "name", children: el.name }), _jsxs("div", { className: "buttons", children: [state.logged?.uid === el.user && (_jsxs(_Fragment, { children: [_jsx(IconButton, { size: "small", onClick: () => handleEdit(el), title: t('tooltip.edit'), name: "edit" }), _jsx(IconButton, { size: "small", onClick: () => handleRemove(el), title: t('tooltip.delete'), name: "delete" })] })), el.requested &&
|
|
101
|
+
!el.read &&
|
|
102
|
+
state.logged?.uid === el.requested.uid && (_jsx(IconButton, { size: "small", onClick: () => {
|
|
103
|
+
handleMarkRead(el);
|
|
104
|
+
}, title: t('tooltip.read'), name: "check_circle" }))] })] }), _jsxs("div", { className: "content", children: [el.title && _jsx("div", { className: "title", children: el.title }), _jsx("div", { className: "text", children: el.value }), el.requested && (_jsxs("div", { className: "date", children: [_jsx("span", { className: "me-1", children: t('label.requested.to') }), _jsx("span", { children: el.requested.name })] })), _jsxs("div", { className: "date", children: [_jsx("span", { className: "me-1", children: t('label.posted.on') }), _jsx("span", { className: "me-1", children: DateUtils.formatDateTime(el.edited ? el.edited : el.date, 'MM/dd/yyyy - HH:mm') }), el.edited && _jsx("i", { children: "edited" })] })] })] }, idx.valueOf())))] })] }) }));
|
|
105
|
+
}
|
|
106
|
+
const getFormSchema = (t) => yup.object().shape({
|
|
107
|
+
title: yup.string().optional(),
|
|
108
|
+
value: yup.string().required(t('common:validation.required')),
|
|
109
|
+
requestedUser: yup.string().optional(),
|
|
110
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Trans, useTranslation } from 'react-i18next';
|
|
4
|
+
import { useNavigate } from 'react-router-dom';
|
|
5
|
+
import { Avatar, IconButton } from 'tycho-storybook';
|
|
6
|
+
import DateUtils from '../../functions/DateUtils';
|
|
7
|
+
import CommentService from '../types/CommentService';
|
|
8
|
+
import './style.scss';
|
|
9
|
+
export default function HeaderNotifications({ lexicon }) {
|
|
10
|
+
const navigate = useNavigate();
|
|
11
|
+
const { t } = useTranslation('comments');
|
|
12
|
+
const [open, setOpen] = useState(false);
|
|
13
|
+
const [notifications, setNotifications] = useState();
|
|
14
|
+
const [comments, setComments] = useState();
|
|
15
|
+
const [tab, setTab] = useState('unread');
|
|
16
|
+
const handleOpen = (notification) => {
|
|
17
|
+
navigate(`/edit/${notification.entry}`);
|
|
18
|
+
setOpen(false);
|
|
19
|
+
};
|
|
20
|
+
const load = () => {
|
|
21
|
+
const serviceMethod = tab === 'read'
|
|
22
|
+
? CommentService.findReadNotifications
|
|
23
|
+
: CommentService.findNotifications;
|
|
24
|
+
serviceMethod(lexicon).then((r) => {
|
|
25
|
+
if (tab !== 'read') {
|
|
26
|
+
setComments(r.data);
|
|
27
|
+
}
|
|
28
|
+
setNotifications(r.data);
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
load();
|
|
33
|
+
}, [lexicon, tab]);
|
|
34
|
+
return (_jsxs("div", { className: "ds-dropdown-container notifications-container", children: [_jsx(IconButton, { name: "notifications_active", className: comments && comments.length > 0 ? 'shake' : '', size: "medium", onClick: () => setOpen(!open) }), open && (_jsx("div", { className: "ds-dropdown-list", children: _jsxs("div", { className: "notifications-panel", children: [_jsxs("div", { className: "header", children: [_jsx("span", { children: t('notification.title') }), _jsx(IconButton, { name: "close", size: "small", mode: "ghost", onClick: () => setOpen(false) })] }), _jsxs("div", { className: "tabs", children: [_jsxs("button", { className: tab === 'unread' ? 'active-tab' : '', onClick: () => setTab('unread'), children: [t('notification.unread'), " (", comments?.length || 0, ")"] }), _jsx("button", { className: tab === 'read' ? 'active-tab' : '', onClick: () => setTab('read'), children: t('notification.all') })] }), _jsxs("div", { className: "content", children: [notifications &&
|
|
35
|
+
notifications.map((not, idx) => (_jsxs("div", { className: "item", onClick: () => handleOpen(not), children: [_jsx(Avatar, { src: not.picture, size: "small" }), _jsxs("div", { className: "message", children: [_jsx("span", { className: "text", children: _jsx(Trans, { t: t, i18nKey: "notification.text", values: {
|
|
36
|
+
user: not.name,
|
|
37
|
+
entry: not.keywords ? not.keywords['entry'] : '',
|
|
38
|
+
} }) }), _jsx("span", { className: "date", children: DateUtils.formatDateTime(not.edited ? not.edited : not.date, 'MM/dd/yyyy - HH:mm') })] })] }, idx.valueOf()))), notifications && notifications.length === 0 && (_jsx("div", { className: "empty-notifications", children: t('notification.empty') }))] })] }) }))] }));
|
|
39
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
@use 'tycho-storybook/dist/styles/main' as *;
|
|
2
|
+
|
|
3
|
+
.notifications-container {
|
|
4
|
+
@keyframes shake {
|
|
5
|
+
0% {
|
|
6
|
+
transform: rotate(0deg);
|
|
7
|
+
}
|
|
8
|
+
20% {
|
|
9
|
+
transform: rotate(-15deg);
|
|
10
|
+
}
|
|
11
|
+
40% {
|
|
12
|
+
transform: rotate(15deg);
|
|
13
|
+
}
|
|
14
|
+
60% {
|
|
15
|
+
transform: rotate(-10deg);
|
|
16
|
+
}
|
|
17
|
+
80% {
|
|
18
|
+
transform: rotate(10deg);
|
|
19
|
+
}
|
|
20
|
+
100% {
|
|
21
|
+
transform: rotate(0deg);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.shake {
|
|
26
|
+
animation: shake 0.5s ease-in-out infinite;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
> .ds-dropdown-list {
|
|
30
|
+
right: 0;
|
|
31
|
+
width: auto;
|
|
32
|
+
|
|
33
|
+
.notifications-panel {
|
|
34
|
+
width: 320px;
|
|
35
|
+
overflow: hidden;
|
|
36
|
+
background: #fff;
|
|
37
|
+
|
|
38
|
+
.header {
|
|
39
|
+
display: flex;
|
|
40
|
+
justify-content: space-between;
|
|
41
|
+
align-items: center;
|
|
42
|
+
padding: 10px 12px;
|
|
43
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
44
|
+
font-weight: bold;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.tabs {
|
|
48
|
+
display: flex;
|
|
49
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
50
|
+
|
|
51
|
+
> button {
|
|
52
|
+
flex: 1;
|
|
53
|
+
padding: 8px;
|
|
54
|
+
border: none;
|
|
55
|
+
background: none;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
font-weight: 500;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.active-tab {
|
|
61
|
+
border-bottom: 2.5px solid var(--border-hover);
|
|
62
|
+
color: var(--icon-accent-hover);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
> .content {
|
|
67
|
+
.item {
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: flex-start;
|
|
70
|
+
gap: 8px;
|
|
71
|
+
cursor: pointer;
|
|
72
|
+
padding: 16px;
|
|
73
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
74
|
+
|
|
75
|
+
&:hover {
|
|
76
|
+
background-color: var(--layer-hover-1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.message {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
gap: 8px;
|
|
83
|
+
|
|
84
|
+
.text {
|
|
85
|
+
font-size: 14px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.date {
|
|
89
|
+
@include helper-small-1;
|
|
90
|
+
color: var(--text-tertiary);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.empty-notifications {
|
|
97
|
+
text-align: center;
|
|
98
|
+
font-size: 13px;
|
|
99
|
+
color: #666;
|
|
100
|
+
padding: 20px 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
@use 'tycho-storybook/dist/styles/main' as *;
|
|
2
|
+
|
|
3
|
+
.comments-container {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
width: 480px;
|
|
7
|
+
|
|
8
|
+
> .header {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
padding: var(--spacing-100);
|
|
12
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
13
|
+
|
|
14
|
+
> .title {
|
|
15
|
+
@include tag-medium-1;
|
|
16
|
+
color: var(--text-secondary);
|
|
17
|
+
margin-left: 8px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
> .actions {
|
|
21
|
+
display: flex;
|
|
22
|
+
margin-left: auto;
|
|
23
|
+
|
|
24
|
+
.edit-button {
|
|
25
|
+
margin-right: var(--spacing-100);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
> .body {
|
|
31
|
+
padding: var(--spacing-0) var(--spacing-250);
|
|
32
|
+
background-color: #fff;
|
|
33
|
+
padding: 16px;
|
|
34
|
+
|
|
35
|
+
.comment {
|
|
36
|
+
display: flex;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
padding: var(--spacing-150) var(--spacing-100);
|
|
39
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
40
|
+
|
|
41
|
+
.top {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
|
|
45
|
+
> .ds-avatar {
|
|
46
|
+
margin-right: 8px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.buttons {
|
|
50
|
+
display: flex;
|
|
51
|
+
margin-left: auto;
|
|
52
|
+
gap: 8px;
|
|
53
|
+
|
|
54
|
+
button {
|
|
55
|
+
border: none;
|
|
56
|
+
border-radius: var(--border-radius-button);
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.content {
|
|
63
|
+
margin-top: 16px;
|
|
64
|
+
|
|
65
|
+
.title {
|
|
66
|
+
@include tag-small-1;
|
|
67
|
+
color: var(--text-accent);
|
|
68
|
+
text-transform: none;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.text {
|
|
72
|
+
@include body-medium-1;
|
|
73
|
+
color: var(--text-primary);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.date {
|
|
77
|
+
@include helper-small-1;
|
|
78
|
+
color: var(--text-tertiary);
|
|
79
|
+
margin-top: 8px;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.form {
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
padding-bottom: 16px;
|
|
88
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
89
|
+
|
|
90
|
+
> .ds-input-text,
|
|
91
|
+
> .ds-select-text {
|
|
92
|
+
margin-bottom: 8px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
> .buttons {
|
|
96
|
+
display: flex;
|
|
97
|
+
gap: 8px;
|
|
98
|
+
|
|
99
|
+
> .ds-button {
|
|
100
|
+
width: 100%;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.placeholder-container {
|
|
106
|
+
margin: 0px;
|
|
107
|
+
font-size: var(--font-size-medium);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.modal-comment-info {
|
|
113
|
+
.modal-body {
|
|
114
|
+
> textarea {
|
|
115
|
+
width: 100%;
|
|
116
|
+
height: 20vh;
|
|
117
|
+
padding: 8px;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.footnote-actions {
|
|
121
|
+
text-align: center;
|
|
122
|
+
|
|
123
|
+
> button {
|
|
124
|
+
font-family: var(--font-family-regular);
|
|
125
|
+
font-style: var(--font-weight-medium);
|
|
126
|
+
font-weight: var(--font-weight-medium);
|
|
127
|
+
font-size: var(--font-size-button);
|
|
128
|
+
color: var(--color-background);
|
|
129
|
+
width: 200px;
|
|
130
|
+
border-radius: 8px;
|
|
131
|
+
padding: 12px;
|
|
132
|
+
border: none;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type CommentRequest = {
|
|
2
|
+
title?: string;
|
|
3
|
+
value: string;
|
|
4
|
+
requestedUser?: string;
|
|
5
|
+
};
|
|
6
|
+
export type Comment = {
|
|
7
|
+
id: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
value: string;
|
|
10
|
+
date: string;
|
|
11
|
+
edited?: string;
|
|
12
|
+
name: string;
|
|
13
|
+
user: string;
|
|
14
|
+
picture: string;
|
|
15
|
+
lexicon: string;
|
|
16
|
+
entry: string;
|
|
17
|
+
requested?: UserComment;
|
|
18
|
+
read?: boolean;
|
|
19
|
+
keywords?: Record<string, string>;
|
|
20
|
+
};
|
|
21
|
+
export type UserComment = {
|
|
22
|
+
uid: string;
|
|
23
|
+
name: string;
|
|
24
|
+
picture: string;
|
|
25
|
+
email: string;
|
|
26
|
+
};
|
|
27
|
+
export declare const EMPTY_COMMENT: Comment;
|
|
28
|
+
export declare const EMPTY_COMMENT_REQUEST: CommentRequest;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import User from '../../configs/User';
|
|
2
|
+
import { Comment, CommentRequest } from './Comment';
|
|
3
|
+
declare function markRead(id: string): Promise<import("axios").AxiosResponse<Comment, any>>;
|
|
4
|
+
declare function update(id: string, comment: CommentRequest): Promise<import("axios").AxiosResponse<Comment, any>>;
|
|
5
|
+
declare function remove(id: string): Promise<import("axios").AxiosResponse<Comment, any>>;
|
|
6
|
+
declare function add(lexicon: string, entry: string, comment: CommentRequest, keywords: Record<string, string | number | boolean>): Promise<import("axios").AxiosResponse<Comment, any>>;
|
|
7
|
+
declare function find(lexicon: string, entry?: string): Promise<import("axios").AxiosResponse<Comment[], any>>;
|
|
8
|
+
declare function findNotifications(lexicon: string): Promise<import("axios").AxiosResponse<Comment[], any>>;
|
|
9
|
+
declare function findReadNotifications(lexicon: string): Promise<import("axios").AxiosResponse<Comment[], any>>;
|
|
10
|
+
declare function findAvailableUsers(lexicon: string): Promise<import("axios").AxiosResponse<User[], any>>;
|
|
11
|
+
declare const CommentService: {
|
|
12
|
+
add: typeof add;
|
|
13
|
+
remove: typeof remove;
|
|
14
|
+
update: typeof update;
|
|
15
|
+
markRead: typeof markRead;
|
|
16
|
+
find: typeof find;
|
|
17
|
+
findNotifications: typeof findNotifications;
|
|
18
|
+
findReadNotifications: typeof findReadNotifications;
|
|
19
|
+
findAvailableUsers: typeof findAvailableUsers;
|
|
20
|
+
};
|
|
21
|
+
export default CommentService;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import api from '../../configs/api';
|
|
2
|
+
function markRead(id) {
|
|
3
|
+
return api.put(`${import.meta.env.VITE_APP_COMMENT_API_URL}/${id}`);
|
|
4
|
+
}
|
|
5
|
+
function update(id, comment) {
|
|
6
|
+
return api.patch(`${import.meta.env.VITE_APP_COMMENT_API_URL}/lexicon/${id}`, { ...comment });
|
|
7
|
+
}
|
|
8
|
+
function remove(id) {
|
|
9
|
+
return api.delete(`${import.meta.env.VITE_APP_COMMENT_API_URL}/${id}`);
|
|
10
|
+
}
|
|
11
|
+
function add(lexicon, entry, comment, keywords) {
|
|
12
|
+
return api.post(`${import.meta.env.VITE_APP_COMMENT_API_URL}/lexicon/${lexicon}`, {
|
|
13
|
+
...comment,
|
|
14
|
+
entry,
|
|
15
|
+
keywords,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function find(lexicon, entry) {
|
|
19
|
+
return api.get(`${import.meta.env.VITE_APP_COMMENT_API_URL}/lexicon/${lexicon}`, {
|
|
20
|
+
params: {
|
|
21
|
+
entry,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function findNotifications(lexicon) {
|
|
26
|
+
return api.get(`${import.meta.env.VITE_APP_COMMENT_API_URL}/lexicon/notifications/${lexicon}`);
|
|
27
|
+
}
|
|
28
|
+
function findReadNotifications(lexicon) {
|
|
29
|
+
return api.get(`${import.meta.env.VITE_APP_COMMENT_API_URL}/lexicon/notifications/read/${lexicon}`);
|
|
30
|
+
}
|
|
31
|
+
function findAvailableUsers(lexicon) {
|
|
32
|
+
return api.get(`${import.meta.env.VITE_APP_AUTH_API}/lexicon/${lexicon}`);
|
|
33
|
+
}
|
|
34
|
+
const CommentService = {
|
|
35
|
+
add,
|
|
36
|
+
remove,
|
|
37
|
+
update,
|
|
38
|
+
markRead,
|
|
39
|
+
find,
|
|
40
|
+
findNotifications,
|
|
41
|
+
findReadNotifications,
|
|
42
|
+
findAvailableUsers,
|
|
43
|
+
};
|
|
44
|
+
export default CommentService;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const KeyboardCustomLayouts = {
|
|
2
|
+
kadiwéu: {
|
|
3
|
+
layout: {
|
|
4
|
+
default: [
|
|
5
|
+
'1 2 3 4 5 6 7 8 9 0',
|
|
6
|
+
'q w e r t y u i o p',
|
|
7
|
+
'a s d f g h j k l',
|
|
8
|
+
'z x c v b n m ǥ',
|
|
9
|
+
'{shift} {space} {bksp}',
|
|
10
|
+
],
|
|
11
|
+
shift: [
|
|
12
|
+
'! @ # $ % ^ & * ( )',
|
|
13
|
+
'Q W E R T Y U I O P',
|
|
14
|
+
'A S D F G H J K L',
|
|
15
|
+
'Z X C V B N M Ǥ',
|
|
16
|
+
'{shift} {space} {bksp}',
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
display: {
|
|
20
|
+
'{bksp}': '⌫',
|
|
21
|
+
'{shift}': '⇧',
|
|
22
|
+
'{space}': '␣',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import 'react-simple-keyboard/build/css/index.css';
|
|
2
|
+
import './style.scss';
|
|
3
|
+
type Props = {
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
defaultLayout: string;
|
|
6
|
+
closeLabel: string;
|
|
7
|
+
};
|
|
8
|
+
export default function VirtualKeyboard({ onClose, defaultLayout, closeLabel, }: Props): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|